@fadyshawky/react-native-magic 2.0.3 → 2.0.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.
Files changed (110) hide show
  1. package/package.json +1 -1
  2. package/template/src/common/ImageResources.g.ts +33 -1
  3. package/template/src/common/components/Background.tsx +3 -1
  4. package/template/src/common/components/Container.tsx +1 -1
  5. package/template/src/common/components/OTPInput.tsx +3 -2
  6. package/template/src/common/components/PrimaryButton.tsx +23 -23
  7. package/template/src/common/components/PrimaryTextInput.tsx +189 -199
  8. package/template/src/common/components/RadioIcon.tsx +4 -4
  9. package/template/src/common/components/SafeText.tsx +41 -0
  10. package/template/src/common/components/SearchBar.tsx +19 -17
  11. package/template/src/common/components/TryAgain.tsx +3 -3
  12. package/template/src/common/localization/localization.ts +10 -0
  13. package/template/src/common/localization/translations/commonLocalization.ts +97 -0
  14. package/template/src/common/localization/translations/homeLocalization.ts +24 -0
  15. package/template/src/common/localization/translations/loginLocalization.ts +28 -2
  16. package/template/src/common/localization/translations/mainNavigationLocalization.ts +30 -0
  17. package/template/src/common/localization/translations/navigationLocalization.ts +48 -0
  18. package/template/src/common/localization/translations/otpLocalization.ts +28 -0
  19. package/template/src/common/localization/translations/passwordLocalization.ts +54 -0
  20. package/template/src/common/localization/translations/posLocalization.ts +196 -0
  21. package/template/src/common/utils/FeesCaalculation.tsx +37 -0
  22. package/template/src/common/utils/index.tsx +11 -0
  23. package/template/src/common/utils/printData.tsx +161 -0
  24. package/template/src/common/validations/errorValidations.ts +3 -2
  25. package/template/src/components/PrinterExample.js +226 -0
  26. package/template/src/core/api/serverHeaders.ts +62 -1
  27. package/template/src/core/store/Categories/categoryActions.ts +33 -0
  28. package/template/src/core/store/Categories/categorySlice.ts +75 -0
  29. package/template/src/core/store/Categories/categoryState.ts +41 -0
  30. package/template/src/core/store/Providers/providersActions.ts +102 -0
  31. package/template/src/core/store/Providers/providersSlice.ts +136 -0
  32. package/template/src/core/store/Providers/providersState.ts +37 -0
  33. package/template/src/core/store/Services/servicesActions.ts +191 -0
  34. package/template/src/core/store/Services/servicesSlice.ts +205 -0
  35. package/template/src/core/store/Services/servicesState.ts +466 -0
  36. package/template/src/core/store/app/appSlice.ts +13 -5
  37. package/template/src/core/store/app/appState.ts +10 -2
  38. package/template/src/core/store/rootReducer.ts +6 -1
  39. package/template/src/core/store/store.tsx +55 -2
  40. package/template/src/core/store/user/userActions.ts +164 -26
  41. package/template/src/core/store/user/userSlice.ts +193 -21
  42. package/template/src/core/store/user/userState.ts +148 -25
  43. package/template/src/core/theme/colors.ts +70 -94
  44. package/template/src/core/theme/commonConsts.ts +1 -1
  45. package/template/src/core/theme/commonSizes.ts +94 -119
  46. package/template/src/core/theme/commonStyles.ts +22 -22
  47. package/template/src/core/theme/fonts.ts +14 -13
  48. package/template/src/core/theme/themes.ts +75 -386
  49. package/template/src/core/theme/types.ts +15 -201
  50. package/template/src/core/utils/stringUtils.ts +114 -0
  51. package/template/src/modules/SunmiCard.js +212 -0
  52. package/template/src/modules/SunmiPrepaid.ts +122 -0
  53. package/template/src/navigation/AuthStack.tsx +8 -0
  54. package/template/src/navigation/HeaderComponents.tsx +76 -1
  55. package/template/src/navigation/MainNavigation.tsx +3 -1
  56. package/template/src/navigation/MainStack.tsx +130 -56
  57. package/template/src/navigation/TabBar.tsx +111 -59
  58. package/template/src/navigation/types.ts +24 -0
  59. package/template/src/screens/Categories/Categories.tsx +141 -0
  60. package/template/src/screens/Categories/hooks/useCategoriesData.ts +33 -0
  61. package/template/src/screens/Categories/types.ts +7 -0
  62. package/template/src/screens/Favorites/Favorites.tsx +130 -0
  63. package/template/src/screens/ForceChangePassword/ForceChangePasswordScreen.tsx +155 -0
  64. package/template/src/screens/History/History.tsx +430 -0
  65. package/template/src/screens/History/hooks/useHistoryData.ts +49 -0
  66. package/template/src/screens/History/types.ts +7 -0
  67. package/template/src/screens/InquiredBill/InquiredBill.tsx +443 -0
  68. package/template/src/screens/InquiredBill/hooks/useInquiredData.ts +91 -0
  69. package/template/src/screens/Login/Login.tsx +85 -85
  70. package/template/src/screens/OTP/OTPScreen.tsx +170 -0
  71. package/template/src/screens/PaymentConfirmation/PaymentConfirmation.tsx +326 -0
  72. package/template/src/screens/Providers/Providers.tsx +166 -0
  73. package/template/src/screens/Providers/hooks/useProvidersData.ts +33 -0
  74. package/template/src/screens/Providers/types.ts +7 -0
  75. package/template/src/screens/ReceiptScreen/ReceiptScreen.tsx +181 -0
  76. package/template/src/screens/ReceiptScreen/hooks/useReceiptData.ts +46 -0
  77. package/template/src/screens/ReceiptScreen/utils/utils.tsx +156 -0
  78. package/template/src/screens/Services/Services.tsx +144 -0
  79. package/template/src/screens/Services/hooks/useServicesData.ts +41 -0
  80. package/template/src/screens/SingleService/Components/FawryInputs.tsx +446 -0
  81. package/template/src/screens/SingleService/SingleService.tsx +229 -0
  82. package/template/src/screens/SingleService/hooks/useServiceData.ts +164 -0
  83. package/template/src/screens/home/Components/PayByCode.tsx +129 -0
  84. package/template/src/screens/home/HomeScreen.tsx +268 -77
  85. package/template/src/screens/home/hooks/useHomeData.ts +32 -38
  86. package/template/src/screens/index.tsx +24 -0
  87. package/template/src/screens/profile/Profile.tsx +290 -2
  88. package/template/src/services/SunmiPrinterInternal.js +268 -0
  89. package/template/src/types/sunmiPrepaid.d.ts +20 -0
  90. package/template/src/utils/SunmiPrinter.ts +442 -0
  91. package/template/src/utils/feesCalculator.ts +92 -0
  92. package/template/src/common/components/Stepper.tsx +0 -153
  93. package/template/src/common/components/Svg.tsx +0 -25
  94. package/template/src/common/hooks/useDebounce.ts +0 -17
  95. package/template/src/common/hooks/usePrevious.ts +0 -11
  96. package/template/src/common/localization/intlFormatter.ts +0 -37
  97. package/template/src/common/urls/emailUrl.ts +0 -20
  98. package/template/src/common/urls/mapUrl.ts +0 -22
  99. package/template/src/common/utils/listHandlers.ts +0 -30
  100. package/template/src/common/utils/serializeQueryParams.ts +0 -10
  101. package/template/src/common/validations/hooks/useDatesError.ts +0 -40
  102. package/template/src/common/validations/profileValidations.ts +0 -30
  103. package/template/src/core/theme/shadows.ts +0 -135
  104. package/template/src/navigation/TopTabBar.tsx +0 -77
  105. package/template/src/screens/Settings/Settings.tsx +0 -5
  106. package/template/src/screens/home/components/CarouselSection.tsx +0 -79
  107. package/template/src/screens/home/components/FeaturedCarousel.tsx +0 -128
  108. package/template/src/screens/main/Main.tsx +0 -5
  109. package/template/src/screens/registration/RegistrationScreen.tsx +0 -198
  110. package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +0 -129
@@ -0,0 +1,442 @@
1
+ import {Alert, NativeModules} from 'react-native';
2
+ import {normalizeAndTrimWhitespace} from '../common/helpers/stringsHelpers';
3
+ import {decryptTripleDES} from '../core/utils/stringUtils';
4
+ import StringBuilder from './stringBuilder';
5
+ const {SunmiPrinter} = NativeModules;
6
+
7
+ // Localized messages for printer status
8
+ const PRINTER_MESSAGES = {
9
+ en: {
10
+ printerStatus_1: 'The printer works normally',
11
+ printerStatus_2: 'Preparing printer',
12
+ printerStatus_3: 'Abnormal communication',
13
+ printerStatus_4: 'The printer is out of paper',
14
+ printerStatus_5: 'Printer overheated',
15
+ printerStatus_6: 'Printer cover is open',
16
+ printerStatus_7: 'Unknown printer error',
17
+ },
18
+ ar: {
19
+ printerStatus_1: 'الطابعة تعمل بشكل طبيعي',
20
+ printerStatus_2: 'جاري تجهيز الطابعة',
21
+ printerStatus_3: 'خطأ في الاتصال بالطابعة',
22
+ printerStatus_4: 'نفذ الورق من الطابعة',
23
+ printerStatus_5: 'الطابعة ساخنة جداً',
24
+ printerStatus_6: 'غطاء الطابعة مفتوح',
25
+ printerStatus_7: 'خطأ غير معروف في الطابعة',
26
+ },
27
+ };
28
+
29
+ interface PrintDataItem {
30
+ type:
31
+ | 'image'
32
+ | 'header1'
33
+ | 'header2'
34
+ | 'body'
35
+ | 'extra_description'
36
+ | 'body_title'
37
+ | 'description';
38
+ value: string;
39
+ label?: string;
40
+ }
41
+
42
+ // Get localized message based on current language
43
+ const getMessage = (key: keyof typeof PRINTER_MESSAGES.en) => {
44
+ // You can get the current language from your app's settings
45
+ // For now, we'll use Arabic as default
46
+ const currentLanguage = 'ar';
47
+ return PRINTER_MESSAGES[currentLanguage][key];
48
+ };
49
+
50
+ type StatusMessageKey =
51
+ | 'printerStatus_1'
52
+ | 'printerStatus_2'
53
+ | 'printerStatus_3'
54
+ | 'printerStatus_4'
55
+ | 'printerStatus_5'
56
+ | 'printerStatus_6'
57
+ | 'printerStatus_7';
58
+
59
+ function createRowSpaces(
60
+ fontSize: number,
61
+ leftText: string,
62
+ rightText: string,
63
+ ) {
64
+ const paperWidth = 390;
65
+ // Calculate character width based on paper width and font size
66
+ const charWidth = 12 * (fontSize / 24); // Base char width is 12px at 24pt
67
+ const maxChars = Math.floor(paperWidth / charWidth);
68
+
69
+ // Format the text with proper spacing
70
+ let row = new StringBuilder();
71
+
72
+ // Truncate texts if they're too long (leaving space for at least 2 characters on each side)
73
+ const maxLeftWidth = maxChars; // Reserve at least 2 chars for right text
74
+ const maxRightWidth = maxChars; // Reserve at least 2 chars for left text
75
+
76
+ const leftPart =
77
+ leftText.length > maxLeftWidth
78
+ ? leftText.substring(0, maxLeftWidth)
79
+ : leftText;
80
+ const rightPart =
81
+ rightText.length > maxRightWidth
82
+ ? rightText.substring(0, maxRightWidth)
83
+ : rightText;
84
+ // Calculate spacing
85
+ const totalTextLength = leftPart.length + rightPart.length;
86
+ const spacesNeeded = maxChars - totalTextLength;
87
+
88
+ // Build the row with proper spacing
89
+ row.append(leftPart);
90
+ for (let i = 0; i < spacesNeeded; i++) {
91
+ row.append(' ');
92
+ }
93
+ row.append(rightPart);
94
+
95
+ return row.toString();
96
+ }
97
+
98
+ export const onBeePrint = async (data: any) => {
99
+ try {
100
+ await SunmiPrinter.prepare();
101
+ await SunmiPrinter.printerInit();
102
+ await SunmiPrinter.setFontStyle('bold', true);
103
+
104
+ const status = await SunmiPrinter.getPrinterStatus();
105
+
106
+ if (status !== 1) {
107
+ Alert.alert(
108
+ getMessage(`printerStatus_${status}` as StatusMessageKey) ||
109
+ getMessage('printerStatus_7'),
110
+ );
111
+ return;
112
+ }
113
+
114
+ // Print the logo at the top
115
+
116
+ let linesText = [];
117
+ let linesAligns = [];
118
+ let linesWidths = [];
119
+ let linesFontSizes = [];
120
+
121
+ for (let i = 0; i < data?.length; i++) {
122
+ if (data[i].image) {
123
+ if (linesText.length > 0) {
124
+ await SunmiPrinter.printColumnsText(
125
+ linesText,
126
+ linesWidths,
127
+ linesAligns,
128
+ linesFontSizes,
129
+ );
130
+ // Clear the arrays after printing
131
+ linesText = [];
132
+ linesAligns = [];
133
+ linesWidths = [];
134
+ linesFontSizes = [];
135
+ }
136
+ // Print the image
137
+ await printImage();
138
+ } else if (data[i].middle) {
139
+ linesText.push(`${data[i].middle}\n`);
140
+ linesAligns.push(1); // Center align
141
+ linesWidths.push(100);
142
+ linesFontSizes.push(34);
143
+ } else if (data[i].middleEnd) {
144
+ linesText.push(`${data[i].middleEnd}\n`);
145
+ linesAligns.push(1); // Center align
146
+ linesWidths.push(100);
147
+ linesFontSizes.push(data[i].fontSize || 34);
148
+ } else if (data[i].line) {
149
+ linesText.push(`--------------------------------\n`);
150
+ linesAligns.push(0); // Left align
151
+ linesWidths.push(384);
152
+ linesFontSizes.push(24);
153
+ } else if (data[i].powered) {
154
+ if (data[i].powered !== '-1') {
155
+ linesText.push(`${data[i].powered}\n`);
156
+ linesAligns.push(1); // Center align
157
+ linesWidths.push(100);
158
+ linesFontSizes.push(data[i].fontSize || 24);
159
+ }
160
+ } else {
161
+ const row = createRowSpaces(
162
+ 24,
163
+ String(data[i].right) || '',
164
+ String(normalizeAndTrimWhitespace(data[i].left)) || '',
165
+ );
166
+ linesText.push(`${row}\n`);
167
+ linesAligns.push(1); // Center align
168
+ linesWidths.push(384);
169
+ linesFontSizes.push(24);
170
+ }
171
+ }
172
+
173
+ if (linesText.length > 0) {
174
+ await SunmiPrinter.printColumnsText(
175
+ linesText,
176
+ linesWidths,
177
+ linesAligns,
178
+ linesFontSizes,
179
+ );
180
+ }
181
+
182
+ // Add minimal spacing at the end for paper cutting
183
+ await SunmiPrinter.lineWrap(3);
184
+ } catch (error) {
185
+ console.error('Printing error:', error);
186
+
187
+ // Display all keys and values from the error object
188
+ if (error && typeof error === 'object') {
189
+ console.error('Error details:');
190
+ for (const [key, value] of Object.entries(error)) {
191
+ console.error(`${key}: ${value}`);
192
+ }
193
+ }
194
+
195
+ Alert.alert('Error', error.message || 'An unexpected error occurred');
196
+ }
197
+ };
198
+
199
+ export const onPrint = async (
200
+ data: {type: string; value: string; label?: string}[],
201
+ ) => {
202
+ try {
203
+ // Initialize printer
204
+ await SunmiPrinter.prepare();
205
+ await SunmiPrinter.printerInit();
206
+ await SunmiPrinter.setFontStyle('bold', true);
207
+
208
+ const status = await SunmiPrinter.getPrinterStatus();
209
+
210
+ if (status !== 1) {
211
+ Alert.alert(
212
+ getMessage(`printerStatus_${status}` as StatusMessageKey) ||
213
+ getMessage('printerStatus_7'),
214
+ );
215
+ return;
216
+ }
217
+
218
+ let linesText = [];
219
+ let linesAligns = [];
220
+ let linesWidths = [];
221
+ let linesFontSizes = [];
222
+
223
+ for (let i = 0; i < data?.length; i++) {
224
+ if (data[i].type === 'image') {
225
+ // First print any accumulated text
226
+ if (linesText.length > 0) {
227
+ await SunmiPrinter.printColumnsText(
228
+ linesText,
229
+ linesWidths,
230
+ linesAligns,
231
+ linesFontSizes,
232
+ );
233
+ // Clear the arrays after printing
234
+ linesText = [];
235
+ linesAligns = [];
236
+ linesWidths = [];
237
+ linesFontSizes = [];
238
+ }
239
+ // Print the image
240
+ await printImage();
241
+ } else if (data[i].type === 'header1') {
242
+ linesText.push(`${normalizeAndTrimWhitespace(data[i].value)}\n`);
243
+ linesAligns.push(1);
244
+ linesWidths.push(100);
245
+ linesFontSizes.push(28);
246
+ } else if (data[i].type === 'header2') {
247
+ linesText.push(`${normalizeAndTrimWhitespace(data[i].value)}\n`);
248
+ linesAligns.push(1);
249
+ linesWidths.push(100);
250
+ linesFontSizes.push(28);
251
+ } else if (
252
+ data[i].type === 'body' ||
253
+ data[i].type === 'extra_description'
254
+ ) {
255
+ const row = createRowSpaces(
256
+ 24,
257
+ String(data[i].label) || '',
258
+ String(normalizeAndTrimWhitespace(data[i].value)) || '',
259
+ );
260
+ linesText.push(`${row}\n`);
261
+ linesAligns.push(1);
262
+ linesWidths.push(384);
263
+ linesFontSizes.push(24);
264
+ } else if (data[i].type === 'body_title') {
265
+ linesText.push(`--------------------------------\n`);
266
+ const row = createRowSpaces(
267
+ 30,
268
+ String(data[i].label) || '',
269
+ String(normalizeAndTrimWhitespace(data[i].value)) || '',
270
+ );
271
+ linesText.push(`${row}\n`);
272
+ linesAligns.push(1);
273
+ linesAligns.push(1);
274
+ linesWidths.push(384);
275
+ linesWidths.push(384);
276
+ linesFontSizes.push(24);
277
+ linesFontSizes.push(30);
278
+ } else if (data[i].type === 'description') {
279
+ linesText.push(`--------------------------------\n`);
280
+ if (data[i].label && data[i].label !== 'VouchPIN') {
281
+ linesText.push(`${data[i].label}\n`);
282
+ linesAligns.push(1);
283
+ linesWidths.push(384);
284
+ linesFontSizes.push(24);
285
+ }
286
+ linesText.push(
287
+ `${
288
+ data[i].label === 'VouchPIN'
289
+ ? decryptTripleDES(
290
+ data[i].value,
291
+ '8pe/hqinrZQV6pHNkQ0WwbD0ZHkaAbbj',
292
+ )
293
+ : normalizeAndTrimWhitespace(data[i].value)
294
+ }\n`,
295
+ );
296
+ linesAligns.push(1);
297
+ linesAligns.push(1);
298
+ linesWidths.push(384);
299
+ linesWidths.push(100);
300
+ linesFontSizes.push(24);
301
+ linesFontSizes.push(24);
302
+ }
303
+ }
304
+
305
+ // Print any remaining text
306
+ if (linesText.length > 0) {
307
+ await SunmiPrinter.printColumnsText(
308
+ linesText,
309
+ linesWidths,
310
+ linesAligns,
311
+ linesFontSizes,
312
+ );
313
+ }
314
+
315
+ // Add minimal spacing at the end for paper cutting
316
+ await SunmiPrinter.lineWrap(3);
317
+ } catch (error) {
318
+ console.error('Printing error:', error);
319
+
320
+ // Display all keys and values from the error object
321
+ if (error && typeof error === 'object') {
322
+ console.error('Error details:');
323
+ for (const [key, value] of Object.entries(error)) {
324
+ console.error(`${key}: ${value}`);
325
+ }
326
+ }
327
+
328
+ // Display a user-friendly alert with the error message
329
+ const errorMessage = error.message || 'An unexpected error occurred';
330
+ Alert.alert('Error', errorMessage);
331
+ }
332
+ };
333
+
334
+ // Helper function to print a single row with left and right aligned text
335
+ export const printRow = async (
336
+ leftText: string,
337
+ rightText: string,
338
+ fontSize = 24,
339
+ ) => {
340
+ try {
341
+ await SunmiPrinter.printRowText(leftText || '', rightText || '', fontSize);
342
+ // Add a small line wrap after each row
343
+ await SunmiPrinter.lineWrap(1);
344
+ } catch (error) {
345
+ console.error('Row printing error:', error);
346
+ }
347
+ };
348
+
349
+ // Helper function to feed paper
350
+ export const feedPaper = async (lines = 1) => {
351
+ try {
352
+ await SunmiPrinter.lineWrap(lines);
353
+ } catch (error) {
354
+ console.error('Paper feed error:', error);
355
+ }
356
+ };
357
+
358
+ // Function to convert image to base64 and print it
359
+ export const printImage = async (width: number = 384) => {
360
+ try {
361
+ // Get the image dimensions
362
+ // const image = Image.resolveAssetSource(
363
+ // ImageResources.receipt_logo_transparent,
364
+ // );
365
+
366
+ // if (!image || !image.uri) {
367
+ // throw new Error('Image asset not found or URI is invalid');
368
+ // }
369
+
370
+ // // Convert image to base64
371
+ // const response = await fetch(image.uri);
372
+ // if (!response.ok) {
373
+ // throw new Error(`Failed to fetch image: ${response.statusText}`);
374
+ // }
375
+
376
+ // const blob = await response.blob();
377
+ // const base64 = await new Promise((resolve, reject) => {
378
+ // const reader = new FileReader();
379
+ // reader.onload = () => resolve(reader.result);
380
+ // reader.onerror = error => {
381
+ // console.error('FileReader error:', error);
382
+ // reject(error);
383
+ // };
384
+ // reader.readAsDataURL(blob);
385
+ // });
386
+
387
+ // Remove the data URL prefix (e.g., "data:image/png;base64,")
388
+ const base64Data =
389
+ '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';
390
+
391
+ // Print the image
392
+ await SunmiPrinter.printLogoText(base64Data, width);
393
+ await feedPaper(2); // Add some space after the image
394
+ } catch (error) {
395
+ console.error('Error printing image:', error);
396
+
397
+ // Display all keys and values from the error object
398
+ if (error && typeof error === 'object') {
399
+ console.error('Error details:');
400
+ for (const [key, value] of Object.entries(error)) {
401
+ console.error(`${key}: ${value}`);
402
+ }
403
+ }
404
+
405
+ throw error;
406
+ }
407
+ };
408
+
409
+ export const SunmiPrinterModule = {
410
+ printColumnsText: async (
411
+ texts: string[],
412
+ widths: number[],
413
+ aligns: number[],
414
+ fontSizes: number[] = Array(texts.length).fill(24), // Default all to 24 if not provided
415
+ ): Promise<void> => {
416
+ return new Promise((resolve, reject) => {
417
+ SunmiPrinter.printColumnsText(
418
+ texts,
419
+ widths,
420
+ aligns,
421
+ fontSizes,
422
+ (error: any) => {
423
+ if (error) {
424
+ reject(error);
425
+ } else {
426
+ resolve();
427
+ }
428
+ },
429
+ );
430
+ });
431
+ },
432
+ };
433
+
434
+ // Example function showing how to use printColumnsText with different font sizes
435
+ export const printExampleReceipt = async () => {
436
+ await SunmiPrinter.printColumnsText(
437
+ ['Item\n', 'Price\n', 'Qty\n'],
438
+ [100, 100, 100],
439
+ [0, 2, 1], // 0=left, 1=center, 2=right
440
+ [24, 32, 24], // Different font sizes for each column
441
+ );
442
+ };
@@ -0,0 +1,92 @@
1
+ import React, {Dispatch, SetStateAction} from 'react';
2
+
3
+ interface FeeTier {
4
+ LowerAmt: number;
5
+ UpperAmt: number;
6
+ FixedAmt: {
7
+ Amt: number;
8
+ CurCode: string;
9
+ };
10
+ }
11
+
12
+ interface FeesCalculationResult {
13
+ amount: number;
14
+ vat_value: number;
15
+ total: number;
16
+ currencyCode: string;
17
+ }
18
+
19
+ // Calculate from amount to total (forward calculation)
20
+ export const calculateFeesForward = (
21
+ amount: string | number,
22
+ vat_value: number,
23
+ currencyCode: string,
24
+ ): FeesCalculationResult => {
25
+ const numericAmount =
26
+ typeof amount === 'string' ? parseFloat(amount) || 0 : amount;
27
+ const total = numericAmount / vat_value;
28
+
29
+ return {
30
+ amount: numericAmount,
31
+ vat_value,
32
+ total,
33
+ currencyCode,
34
+ };
35
+ };
36
+
37
+ // Calculate from total to amount (backward calculation)
38
+ export const calculateFeesBackward = (
39
+ total: string | number,
40
+ vat_value: number,
41
+ currencyCode: string,
42
+ ): FeesCalculationResult => {
43
+ const numericTotal =
44
+ typeof total === 'string' ? parseFloat(total) || 0 : total;
45
+ const amount = numericTotal * vat_value;
46
+
47
+ return {
48
+ amount,
49
+ vat_value,
50
+ total: numericTotal,
51
+ currencyCode,
52
+ };
53
+ };
54
+
55
+ // Hook for bi-directional calculation
56
+ export const useFeesCalculation = (
57
+ amount: string,
58
+ vat_value: number,
59
+ currencyCode: string,
60
+ vatValue: string,
61
+ setVatValue: Dispatch<SetStateAction<string>>,
62
+ setAmount: Dispatch<SetStateAction<string>>,
63
+ ) => {
64
+ const calculateFromAmount = (amountValue: string) => {
65
+ if (!amountValue || !vat_value) {
66
+ setVatValue('');
67
+ return;
68
+ }
69
+ const numericAmount = parseFloat(amountValue);
70
+ if (!isNaN(numericAmount)) {
71
+ const total = numericAmount / vat_value;
72
+ setVatValue(total.toFixed(1));
73
+ }
74
+ };
75
+
76
+ const calculateFromVat = (vatVal: string) => {
77
+ if (!vatVal || !vat_value) {
78
+ setAmount('');
79
+ return;
80
+ }
81
+ const numericTotal = parseFloat(vatVal);
82
+ if (!isNaN(numericTotal)) {
83
+ const calculatedAmount = numericTotal * vat_value;
84
+ setAmount(calculatedAmount.toFixed(1));
85
+ }
86
+ };
87
+
88
+ return {
89
+ calculateFromAmount,
90
+ calculateFromVat,
91
+ };
92
+ };