digipay-utility-payment 0.0.12 → 0.0.14
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.
|
Binary file
|
|
@@ -736,7 +736,8 @@ class TranslationService {
|
|
|
736
736
|
COMMON_LABEL_CLOSE: 'Close',
|
|
737
737
|
COMMON_LABEL_AMOUNT_MUST_NOT_EXCEED_BILL_AMOUNT: 'Amount must not be greater than the bill amount',
|
|
738
738
|
LABEL_NO_PROVIDERS_AVAILABLE: 'No providers available',
|
|
739
|
-
LABEL_ENTER_4_DIGIT_TRANSACTION_PIN: 'Enter your 4-digit transaction PIN'
|
|
739
|
+
LABEL_ENTER_4_DIGIT_TRANSACTION_PIN: 'Enter your 4-digit transaction PIN',
|
|
740
|
+
AUTH_INVALID_TRANSACTION_PIN: "Invalid transaction PIN",
|
|
740
741
|
};
|
|
741
742
|
translate(key) {
|
|
742
743
|
return TranslationService.LABELS[key] ?? key;
|
|
@@ -1104,7 +1105,6 @@ function utilityPaymentsSdkDebug(event, detail) {
|
|
|
1104
1105
|
if (!always && !utilityPaymentsSdkIsVerboseDebug()) {
|
|
1105
1106
|
return;
|
|
1106
1107
|
}
|
|
1107
|
-
console.info(LOG_PREFIX, event, { tag: UTILITY_PAYMENTS_SDK_BUILD_TAG, ...detail });
|
|
1108
1108
|
}
|
|
1109
1109
|
|
|
1110
1110
|
class VendorProvidersComponent {
|
|
@@ -2277,239 +2277,231 @@ class BillWorkflowComponent {
|
|
|
2277
2277
|
};
|
|
2278
2278
|
this.topupAndBillpaymentService.saveWorkflowData(workflowState);
|
|
2279
2279
|
}
|
|
2280
|
-
getExtraFieldsToShow(field) {
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
minLength = parseInt(fieldValue);
|
|
2314
|
-
if (fieldError)
|
|
2315
|
-
validationMessages.minlength = fieldError;
|
|
2316
|
-
if (!isRequired)
|
|
2317
|
-
isRequired = true;
|
|
2318
|
-
}
|
|
2319
|
-
else if (fieldType === 5) {
|
|
2320
|
-
maxValue = parseFloat(fieldValue);
|
|
2321
|
-
if (fieldError)
|
|
2322
|
-
validationMessages.max = fieldError;
|
|
2323
|
-
}
|
|
2324
|
-
});
|
|
2325
|
-
let res = {
|
|
2326
|
-
key: field.external_field_key,
|
|
2327
|
-
templateOptions: {},
|
|
2328
|
-
className: "col-sm-6",
|
|
2329
|
-
wrappers: ["form-field-horizontal"],
|
|
2330
|
-
type: "input",
|
|
2331
|
-
modelOptions: { updateOn: 'change' },
|
|
2332
|
-
expressionProperties: {
|
|
2333
|
-
'templateOptions.disabled': 'formState.disabled',
|
|
2334
|
-
},
|
|
2335
|
-
hooks: {
|
|
2336
|
-
onInit: (field) => {
|
|
2337
|
-
const control = field.formControl;
|
|
2338
|
-
if (control) {
|
|
2339
|
-
control.valueChanges.subscribe(() => {
|
|
2340
|
-
if (control.invalid && control.value !== null && control.value !== '') {
|
|
2341
|
-
control.markAsTouched();
|
|
2342
|
-
}
|
|
2343
|
-
});
|
|
2344
|
-
}
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
};
|
|
2348
|
-
if (isRequired) {
|
|
2349
|
-
validationMessages.required = `${field.field_title} ${this.translationService.translate("LABEL_IS_REQUIRED")}`;
|
|
2350
|
-
}
|
|
2351
|
-
if (Object.keys(validationMessages).length > 0) {
|
|
2352
|
-
res["validation"] = { messages: validationMessages };
|
|
2353
|
-
}
|
|
2354
|
-
if (field.field_type == TOPUP_BILL_PAYMENT_FIELD_TYPE.TEXT_BOX) {
|
|
2355
|
-
res["templateOptions"] = {
|
|
2356
|
-
label: `${field.field_title} `,
|
|
2357
|
-
placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2358
|
-
required: isRequired,
|
|
2359
|
-
minLength: minLength,
|
|
2360
|
-
maxLength: maxLength,
|
|
2361
|
-
readonly: isReadOnly,
|
|
2362
|
-
};
|
|
2363
|
-
}
|
|
2364
|
-
else if (field.field_type == TOPUP_BILL_PAYMENT_FIELD_TYPE.NUMERIC) {
|
|
2365
|
-
res["templateOptions"] = {
|
|
2366
|
-
label: `${field.field_title} `,
|
|
2367
|
-
placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2368
|
-
required: isRequired,
|
|
2369
|
-
min: minValue,
|
|
2370
|
-
max: maxValue,
|
|
2371
|
-
minLength: minLength,
|
|
2372
|
-
maxLength: maxLength,
|
|
2373
|
-
};
|
|
2374
|
-
res["type"] = "number-field";
|
|
2375
|
-
}
|
|
2376
|
-
return res; // Always reached now
|
|
2377
|
-
}
|
|
2378
|
-
// getExtraFieldsToShow(field: any): FormlyFieldConfig | null {
|
|
2379
|
-
// if (field) {
|
|
2380
|
-
// let isReadOnly = false;
|
|
2381
|
-
// let fieldValue: any = null;
|
|
2382
|
-
// let customValidators: any = {};
|
|
2383
|
-
// if (field?.external_field_key === "dial_code") {
|
|
2384
|
-
// isReadOnly = true;
|
|
2385
|
-
// fieldValue = this.currentUserDetails?.dial_code;
|
|
2386
|
-
// this.fieldsModel = { "dial_code": fieldValue };
|
|
2280
|
+
// getExtraFieldsToShow(field: any): FormlyFieldConfig | undefined {
|
|
2281
|
+
// if (!field) return undefined; // Early return for falsy field
|
|
2282
|
+
// let isReadOnly = false;
|
|
2283
|
+
// if (field?.external_field_key === "dial_code") {
|
|
2284
|
+
// isReadOnly = true;
|
|
2285
|
+
// this.fieldsModel = { "dial_code": this.currentUserDetails?.dial_code };
|
|
2286
|
+
// }
|
|
2287
|
+
// const validations: Array<any> = field?.field_validation || [];
|
|
2288
|
+
// let isRequired: boolean = false;
|
|
2289
|
+
// let minLength: number | undefined;
|
|
2290
|
+
// let maxLength: number | undefined;
|
|
2291
|
+
// let minValue: number | undefined;
|
|
2292
|
+
// let maxValue: number | undefined;
|
|
2293
|
+
// const validationMessages: any = {};
|
|
2294
|
+
// validations.forEach((validation: any) => {
|
|
2295
|
+
// const fieldType = validation?.field_types;
|
|
2296
|
+
// const fieldValue = validation?.field_values;
|
|
2297
|
+
// const fieldError = validation?.field_error;
|
|
2298
|
+
// if (fieldType === 1) {
|
|
2299
|
+
// isRequired = fieldValue === "true" || fieldValue === true || fieldValue === "1";
|
|
2300
|
+
// } else if (fieldType === 2) {
|
|
2301
|
+
// maxLength = parseInt(fieldValue);
|
|
2302
|
+
// if (fieldError) validationMessages.maxlength = fieldError;
|
|
2303
|
+
// } else if (fieldType === 3) {
|
|
2304
|
+
// minValue = parseFloat(fieldValue);
|
|
2305
|
+
// if (fieldError) validationMessages.min = fieldError;
|
|
2306
|
+
// } else if (fieldType === 4) {
|
|
2307
|
+
// minLength = parseInt(fieldValue);
|
|
2308
|
+
// if (fieldError) validationMessages.minlength = fieldError;
|
|
2309
|
+
// if (!isRequired) isRequired = true;
|
|
2310
|
+
// } else if (fieldType === 5) {
|
|
2311
|
+
// maxValue = parseFloat(fieldValue);
|
|
2312
|
+
// if (fieldError) validationMessages.max = fieldError;
|
|
2387
2313
|
// }
|
|
2388
|
-
//
|
|
2389
|
-
//
|
|
2390
|
-
//
|
|
2391
|
-
//
|
|
2392
|
-
//
|
|
2393
|
-
//
|
|
2394
|
-
//
|
|
2395
|
-
//
|
|
2396
|
-
//
|
|
2397
|
-
//
|
|
2398
|
-
//
|
|
2399
|
-
//
|
|
2314
|
+
// });
|
|
2315
|
+
// let res: FormlyFieldConfig = {
|
|
2316
|
+
// key: field.external_field_key,
|
|
2317
|
+
// templateOptions: {} as FormlyTemplateOptions,
|
|
2318
|
+
// className: "col-sm-6",
|
|
2319
|
+
// wrappers: ["form-field-horizontal"],
|
|
2320
|
+
// type: "input",
|
|
2321
|
+
// modelOptions: { updateOn: 'change' },
|
|
2322
|
+
// expressionProperties: {
|
|
2323
|
+
// 'templateOptions.disabled': 'formState.disabled',
|
|
2324
|
+
// },
|
|
2325
|
+
// hooks: {
|
|
2326
|
+
// onInit: (field: FormlyFieldConfig) => {
|
|
2327
|
+
// const control = field.formControl;
|
|
2328
|
+
// if (control) {
|
|
2329
|
+
// control.valueChanges.subscribe(() => {
|
|
2330
|
+
// if (control.invalid && control.value !== null && control.value !== '') {
|
|
2331
|
+
// control.markAsTouched();
|
|
2332
|
+
// }
|
|
2333
|
+
// });
|
|
2400
2334
|
// }
|
|
2401
|
-
// } else if (this.selectedProduct?.product_price_type === PRODUCT_PRICE_TYPE.RANGE) {
|
|
2402
|
-
// const rangeMin = Number(this.selectedProduct?.product_range_price?.min) || 0;
|
|
2403
|
-
// const rangeMax = Number(this.selectedProduct?.product_range_price?.max) || 0;
|
|
2404
|
-
// customValidators = {
|
|
2405
|
-
// // Range validator - checks if value is within the allowed range
|
|
2406
|
-
// // Allows typing leading zeros (e.g., "01", "08") but validates the numeric value
|
|
2407
|
-
// range: {
|
|
2408
|
-
// expression: (control: any) => {
|
|
2409
|
-
// const value = control?.value;
|
|
2410
|
-
// if (value === null || value === undefined || value === '') {
|
|
2411
|
-
// return true; // No error if empty
|
|
2412
|
-
// }
|
|
2413
|
-
// const valueStr = String(value).trim();
|
|
2414
|
-
// // Convert string to number (handles leading zeros: "01" becomes 1, "08" becomes 8)
|
|
2415
|
-
// const numValue = Number(valueStr);
|
|
2416
|
-
// if (isNaN(numValue) || !isFinite(numValue)) {
|
|
2417
|
-
// return true; // Skip validation for invalid numbers
|
|
2418
|
-
// }
|
|
2419
|
-
// // Return true if value is IN range (no error), false if out of range (error)
|
|
2420
|
-
// return numValue >= rangeMin && numValue <= rangeMax;
|
|
2421
|
-
// },
|
|
2422
|
-
// message: () => `${this.translationService.translate("COMMON_LABEL_AMOUNT_MUST_BE_BETWEEN")} ${rangeMin} ${this.translationService.translate("COMMON_LABEL_AND")} ${rangeMax}`
|
|
2423
|
-
// }
|
|
2424
|
-
// };
|
|
2425
2335
|
// }
|
|
2426
2336
|
// }
|
|
2427
|
-
//
|
|
2428
|
-
//
|
|
2429
|
-
//
|
|
2430
|
-
//
|
|
2431
|
-
//
|
|
2432
|
-
//
|
|
2433
|
-
//
|
|
2434
|
-
//
|
|
2435
|
-
//
|
|
2436
|
-
//
|
|
2437
|
-
//
|
|
2438
|
-
//
|
|
2439
|
-
//
|
|
2440
|
-
//
|
|
2441
|
-
//
|
|
2442
|
-
// let res: FormlyFieldConfig = {
|
|
2443
|
-
// key: field.external_field_key,
|
|
2444
|
-
// templateOptions: {} as any,
|
|
2445
|
-
// className: "col-12",
|
|
2446
|
-
// wrappers: ["form-field-horizontal"],
|
|
2447
|
-
// type: "input",
|
|
2448
|
-
// // Enable real-time validation on change/input
|
|
2449
|
-
// modelOptions: {
|
|
2450
|
-
// updateOn: 'change'
|
|
2451
|
-
// },
|
|
2452
|
-
// // Show errors in real-time (even when not touched)
|
|
2453
|
-
// expressionProperties: {
|
|
2454
|
-
// 'templateOptions.disabled': 'formState.disabled',
|
|
2455
|
-
// },
|
|
2456
|
-
// // Mark field as touched on value change to show errors immediately
|
|
2457
|
-
// hooks: {
|
|
2458
|
-
// onInit: (field: FormlyFieldConfig) => {
|
|
2459
|
-
// const control = field.formControl;
|
|
2460
|
-
// if (control) {
|
|
2461
|
-
// // Subscribe to value changes and mark as touched to show errors immediately
|
|
2462
|
-
// control.valueChanges.subscribe(() => {
|
|
2463
|
-
// if (control.invalid && control.value !== null && control.value !== '') {
|
|
2464
|
-
// control.markAsTouched();
|
|
2465
|
-
// }
|
|
2466
|
-
// });
|
|
2467
|
-
// }
|
|
2468
|
-
// }
|
|
2469
|
-
// }
|
|
2337
|
+
// };
|
|
2338
|
+
// if (isRequired) {
|
|
2339
|
+
// validationMessages.required = `${field.field_title} ${this.translationService.translate("LABEL_IS_REQUIRED")}`;
|
|
2340
|
+
// }
|
|
2341
|
+
// if (Object.keys(validationMessages).length > 0) {
|
|
2342
|
+
// res["validation"] = { messages: validationMessages };
|
|
2343
|
+
// }
|
|
2344
|
+
// if (field.field_type == TOPUP_BILL_PAYMENT_FIELD_TYPE.TEXT_BOX) {
|
|
2345
|
+
// res["templateOptions"] = {
|
|
2346
|
+
// label: `${field.field_title} `,
|
|
2347
|
+
// placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2348
|
+
// required: isRequired,
|
|
2349
|
+
// minLength: minLength,
|
|
2350
|
+
// maxLength: maxLength,
|
|
2351
|
+
// readonly: isReadOnly,
|
|
2470
2352
|
// };
|
|
2471
|
-
//
|
|
2472
|
-
//
|
|
2473
|
-
//
|
|
2474
|
-
//
|
|
2475
|
-
//
|
|
2476
|
-
//
|
|
2477
|
-
//
|
|
2478
|
-
//
|
|
2479
|
-
//
|
|
2480
|
-
//
|
|
2481
|
-
//
|
|
2482
|
-
// readonly: isReadOnly,
|
|
2483
|
-
// };
|
|
2484
|
-
// if (fieldValue !== null) {
|
|
2485
|
-
// res["defaultValue"] = fieldValue;
|
|
2486
|
-
// }
|
|
2487
|
-
// } else if (normalizedFieldType == TOPUP_BILL_PAYMENT_FIELD_TYPE.NUMERIC) {
|
|
2488
|
-
// // For range products, don't set min/max in templateOptions to allow typing
|
|
2489
|
-
// // Custom validator will handle validation
|
|
2490
|
-
// const templateOptions: any = {
|
|
2491
|
-
// label: `${field.field_title} `,
|
|
2492
|
-
// placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2493
|
-
// required: isRequired,
|
|
2494
|
-
// minLength: minLength,
|
|
2495
|
-
// maxLength: maxLength,
|
|
2496
|
-
// readonly: isReadOnly,
|
|
2497
|
-
// };
|
|
2498
|
-
// // Only set min/max if NOT a range product (to allow typing any value for range products)
|
|
2499
|
-
// if (!(isAmountField && this.selectedProduct?.product_price_type === PRODUCT_PRICE_TYPE.RANGE)) {
|
|
2500
|
-
// templateOptions.min = minValue;
|
|
2501
|
-
// templateOptions.max = maxValue;
|
|
2502
|
-
// }
|
|
2503
|
-
// res["templateOptions"] = templateOptions;
|
|
2504
|
-
// res["type"] = "number-field";
|
|
2505
|
-
// if (fieldValue !== null) {
|
|
2506
|
-
// res["defaultValue"] = fieldValue;
|
|
2507
|
-
// }
|
|
2508
|
-
// }
|
|
2509
|
-
// return res;
|
|
2353
|
+
// } else if (field.field_type == TOPUP_BILL_PAYMENT_FIELD_TYPE.NUMERIC) {
|
|
2354
|
+
// res["templateOptions"] = {
|
|
2355
|
+
// label: `${field.field_title} `,
|
|
2356
|
+
// placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2357
|
+
// required: isRequired,
|
|
2358
|
+
// min: minValue,
|
|
2359
|
+
// max: maxValue,
|
|
2360
|
+
// minLength: minLength,
|
|
2361
|
+
// maxLength: maxLength,
|
|
2362
|
+
// };
|
|
2363
|
+
// res["type"] = "number-field";
|
|
2510
2364
|
// }
|
|
2511
|
-
// return
|
|
2365
|
+
// return res; // Always reached now
|
|
2512
2366
|
// }
|
|
2367
|
+
getExtraFieldsToShow(field) {
|
|
2368
|
+
if (field) {
|
|
2369
|
+
let isReadOnly = false;
|
|
2370
|
+
let fieldValue = null;
|
|
2371
|
+
let customValidators = {};
|
|
2372
|
+
if (field?.external_field_key === "dial_code") {
|
|
2373
|
+
isReadOnly = true;
|
|
2374
|
+
fieldValue = this.currentUserDetails?.dial_code;
|
|
2375
|
+
this.fieldsModel = { "dial_code": fieldValue };
|
|
2376
|
+
}
|
|
2377
|
+
// Check if this is an amount field and we have a selected product
|
|
2378
|
+
const isAmountField = field?.external_field_key?.toLowerCase().includes("amount") ||
|
|
2379
|
+
field?.field_title?.toLowerCase().includes("amount");
|
|
2380
|
+
if (isAmountField && this.selectedProduct) {
|
|
2381
|
+
if (this.selectedProduct?.product_price_type === PRODUCT_PRICE_TYPE.FIXED) {
|
|
2382
|
+
// Fixed price: make field readonly and set the fixed price
|
|
2383
|
+
isReadOnly = true;
|
|
2384
|
+
fieldValue = this.selectedProduct?.product_fixed_price || 0;
|
|
2385
|
+
if (this.fieldsModel) {
|
|
2386
|
+
this.fieldsModel[field.external_field_key] = fieldValue;
|
|
2387
|
+
}
|
|
2388
|
+
else {
|
|
2389
|
+
this.fieldsModel = { [field.external_field_key]: fieldValue };
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
else if (this.selectedProduct?.product_price_type === PRODUCT_PRICE_TYPE.RANGE) {
|
|
2393
|
+
const rangeMin = Number(this.selectedProduct?.product_range_price?.min) || 0;
|
|
2394
|
+
const rangeMax = Number(this.selectedProduct?.product_range_price?.max) || 0;
|
|
2395
|
+
customValidators = {
|
|
2396
|
+
// Range validator - checks if value is within the allowed range
|
|
2397
|
+
// Allows typing leading zeros (e.g., "01", "08") but validates the numeric value
|
|
2398
|
+
range: {
|
|
2399
|
+
expression: (control) => {
|
|
2400
|
+
const value = control?.value;
|
|
2401
|
+
if (value === null || value === undefined || value === '') {
|
|
2402
|
+
return true; // No error if empty
|
|
2403
|
+
}
|
|
2404
|
+
const valueStr = String(value).trim();
|
|
2405
|
+
// Convert string to number (handles leading zeros: "01" becomes 1, "08" becomes 8)
|
|
2406
|
+
const numValue = Number(valueStr);
|
|
2407
|
+
if (isNaN(numValue) || !isFinite(numValue)) {
|
|
2408
|
+
return true; // Skip validation for invalid numbers
|
|
2409
|
+
}
|
|
2410
|
+
// Return true if value is IN range (no error), false if out of range (error)
|
|
2411
|
+
return numValue >= rangeMin && numValue <= rangeMax;
|
|
2412
|
+
},
|
|
2413
|
+
message: () => `${this.translationService.translate("COMMON_LABEL_AMOUNT_MUST_BE_BETWEEN")} ${rangeMin} ${this.translationService.translate("COMMON_LABEL_AND")} ${rangeMax}`
|
|
2414
|
+
}
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
const validations = field?.field_validation || [];
|
|
2419
|
+
const normalizedFieldType = this.normalizeFieldType(field?.field_type);
|
|
2420
|
+
const normalizedValidations = this.normalizeValidations(validations);
|
|
2421
|
+
const isRequired = normalizedValidations.isRequired || true;
|
|
2422
|
+
const minLength = normalizedValidations.minLength;
|
|
2423
|
+
const maxLength = normalizedValidations.maxLength;
|
|
2424
|
+
let minValue = normalizedValidations.minValue;
|
|
2425
|
+
let maxValue = normalizedValidations.maxValue;
|
|
2426
|
+
// For range products, don't set HTML5 min/max to allow typing any value
|
|
2427
|
+
// We'll validate using custom validator only
|
|
2428
|
+
// This prevents HTML5 from blocking input before validation
|
|
2429
|
+
if (isAmountField && this.selectedProduct?.product_price_type === PRODUCT_PRICE_TYPE.RANGE) {
|
|
2430
|
+
// Don't override min/max - let custom validator handle it
|
|
2431
|
+
// This allows users to type any value, then validate on blur/submit
|
|
2432
|
+
}
|
|
2433
|
+
let res = {
|
|
2434
|
+
key: field.external_field_key,
|
|
2435
|
+
templateOptions: {},
|
|
2436
|
+
className: "col-12",
|
|
2437
|
+
wrappers: ["form-field-horizontal"],
|
|
2438
|
+
type: "input",
|
|
2439
|
+
// Enable real-time validation on change/input
|
|
2440
|
+
modelOptions: {
|
|
2441
|
+
updateOn: 'change'
|
|
2442
|
+
},
|
|
2443
|
+
// Show errors in real-time (even when not touched)
|
|
2444
|
+
expressionProperties: {
|
|
2445
|
+
'templateOptions.disabled': 'formState.disabled',
|
|
2446
|
+
},
|
|
2447
|
+
// Mark field as touched on value change to show errors immediately
|
|
2448
|
+
hooks: {
|
|
2449
|
+
onInit: (field) => {
|
|
2450
|
+
const control = field.formControl;
|
|
2451
|
+
if (control) {
|
|
2452
|
+
// Subscribe to value changes and mark as touched to show errors immediately
|
|
2453
|
+
control.valueChanges.subscribe(() => {
|
|
2454
|
+
if (control.invalid && control.value !== null && control.value !== '') {
|
|
2455
|
+
control.markAsTouched();
|
|
2456
|
+
}
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
};
|
|
2462
|
+
// Add custom validators if any
|
|
2463
|
+
if (Object.keys(customValidators).length > 0) {
|
|
2464
|
+
res["validators"] = customValidators;
|
|
2465
|
+
}
|
|
2466
|
+
if (normalizedFieldType == TOPUP_BILL_PAYMENT_FIELD_TYPE.TEXT_BOX) {
|
|
2467
|
+
res["templateOptions"] = {
|
|
2468
|
+
label: `${field.field_title} `,
|
|
2469
|
+
placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2470
|
+
required: isRequired,
|
|
2471
|
+
minLength: minLength,
|
|
2472
|
+
maxLength: maxLength,
|
|
2473
|
+
readonly: isReadOnly,
|
|
2474
|
+
};
|
|
2475
|
+
if (fieldValue !== null) {
|
|
2476
|
+
res["defaultValue"] = fieldValue;
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
else if (normalizedFieldType == TOPUP_BILL_PAYMENT_FIELD_TYPE.NUMERIC) {
|
|
2480
|
+
// For range products, don't set min/max in templateOptions to allow typing
|
|
2481
|
+
// Custom validator will handle validation
|
|
2482
|
+
const templateOptions = {
|
|
2483
|
+
label: `${field.field_title} `,
|
|
2484
|
+
placeholder: `${this.translationService.translate("COMMON_LABEL_PLEASE_ENTER")} ${field.field_title}`,
|
|
2485
|
+
required: isRequired,
|
|
2486
|
+
minLength: minLength,
|
|
2487
|
+
maxLength: maxLength,
|
|
2488
|
+
readonly: isReadOnly,
|
|
2489
|
+
};
|
|
2490
|
+
// Only set min/max if NOT a range product (to allow typing any value for range products)
|
|
2491
|
+
if (!(isAmountField && this.selectedProduct?.product_price_type === PRODUCT_PRICE_TYPE.RANGE)) {
|
|
2492
|
+
templateOptions.min = minValue;
|
|
2493
|
+
templateOptions.max = maxValue;
|
|
2494
|
+
}
|
|
2495
|
+
res["templateOptions"] = templateOptions;
|
|
2496
|
+
res["type"] = "number-field";
|
|
2497
|
+
if (fieldValue !== null) {
|
|
2498
|
+
res["defaultValue"] = fieldValue;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
return res;
|
|
2502
|
+
}
|
|
2503
|
+
return null;
|
|
2504
|
+
}
|
|
2513
2505
|
ngOnDestroy() {
|
|
2514
2506
|
this.destroy$.next(true);
|
|
2515
2507
|
this.destroy$.unsubscribe();
|
|
@@ -3065,6 +3057,21 @@ class WorkFlowComponent {
|
|
|
3065
3057
|
return TOPUP_BILL_PAYMENT_FIELD_TYPE.BUTTON;
|
|
3066
3058
|
return fieldTypeValue;
|
|
3067
3059
|
}
|
|
3060
|
+
isFormIncomplete() {
|
|
3061
|
+
if (!this.detailsForm)
|
|
3062
|
+
return true;
|
|
3063
|
+
let hasEmptyFields = false;
|
|
3064
|
+
this.fieldsArray.forEach((field) => {
|
|
3065
|
+
const isRequired = field?.templateOptions?.required;
|
|
3066
|
+
const fieldKey = field.key != null ? String(field.key) : '';
|
|
3067
|
+
const formControl = this.detailsForm.get(fieldKey);
|
|
3068
|
+
const fieldValue = formControl?.value;
|
|
3069
|
+
if ((!fieldValue || fieldValue === '' || fieldValue === null || fieldValue === undefined)) {
|
|
3070
|
+
hasEmptyFields = true;
|
|
3071
|
+
}
|
|
3072
|
+
});
|
|
3073
|
+
return hasEmptyFields || !this.detailsForm.valid;
|
|
3074
|
+
}
|
|
3068
3075
|
normalizeFieldAction(action) {
|
|
3069
3076
|
const actionValue = String(action ?? "").trim().toUpperCase();
|
|
3070
3077
|
if (actionValue === "GET_BILL_SCREEN")
|
|
@@ -3116,7 +3123,7 @@ class WorkFlowComponent {
|
|
|
3116
3123
|
},
|
|
3117
3124
|
],
|
|
3118
3125
|
}),
|
|
3119
|
-
], ngImport: i0, template: "\n\n@if (showWorkflowComponent) {\n \n <sdk-bill-workflow \n [selectedBill]=\"selectedBillForWorkflow\"\n (workflowCompleted)=\"onWorkflowCompleted($event)\">\n </sdk-bill-workflow>\n \n \n}@else { \n <div class=\"content\">\n <header class=\"page-header\">\n <div class=\"d-flex align-items-center flex-wrap\">\n <div class=\"mr-auto\">\n <h1>{{ 'LABEL_UTILITIES' | translate }}</h1>\n </div>\n </div>\n </header>\n \n <section class=\"page-content container-fluid\">\n \n <div class=\"card m-0 border-0\">\n <div class=\"card-header p-t-25 p-b-20\">\n <h4 class=\"card-title font-weight-600 font-size-18 m-0 text-dark\">{{ vendorProvider?.provider_name }}\n </h4>\n </div>\n <div class=\"tab-content\">\n <div class=\"tab-pane fadeIn active show\">\n <form role=\"form\" class=\"form-horizontal\" [formGroup]=\"detailsForm\" (ngSubmit)=\"submitForm()\">\n <div class=\"card-body\">\n <div class=\"mt-3\">\n @if (fieldsArray.length > 0) {\n <div class=\"row\">\n <div class=\"col-xl-5 offset-xl-3\">\n <!-- <div class=\"form-group form-row\">\n <label\n class=\"control-label text-md-right col-md-4 col-lg-3\">Provider</label>\n <div class=\"col-md-5\">\n <ng-select placeholder=\"Select Provider\" bindLabel=\"provider_name\"\n bindValue=\"id\" [items]=\"vendorProviders\"\n (change)=\"changeVendorProvider($event)\">\n </ng-select>\n </div>\n </div> -->\n <!-- <pre>{{fieldsArray|json}}</pre> -->\n <formly-form [fields]=\"fieldsArray\" [model]=\"fieldsModel\" [form]=\"detailsForm\">\n </formly-form>\n </div>\n </div>\n }\n \n @if (fieldsArray.length === 0) {\n <sdk-no-data title=\"LABEL_FIELDS_NOT_AVAILABLE\"></sdk-no-data>\n }\n </div>\n </div>\n <div class=\"card-footer bg-light text-right\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"back()\">\n {{'COMMON_LABEL_CANCEL' | translate}}\n </button>\n @if (buttonData) {\n <button type=\"submit\" class=\"btn btn-primary\"\n [ngClass]=\"{'qt-loader qt-loader-mini qt-loader-left': showLoader}\"\n [disabled]=\"showLoader\">\n {{ buttonData?.field_title }}\n </button>\n }\n </div>\n </form>\n </div>\n </div>\n </div>\n </section>\n </div>\n}", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SharedModule }, { kind: "ngmodule", type: NgSelectModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyBootstrapModule }, { kind: "ngmodule", type: FormlyModule }, { kind: "component", type: i1$2.LegacyFormlyForm, selector: "formly-form" }, { kind: "component", type: NoDataComponent, selector: "sdk-no-data", inputs: ["title", "icon"] }, { kind: "component", type: BillWorkflowComponent, selector: "sdk-bill-workflow", inputs: ["billData", "selectedBill"], outputs: ["workflowCompleted"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
3126
|
+
], ngImport: i0, template: "\n\n@if (showWorkflowComponent) {\n \n <sdk-bill-workflow \n [selectedBill]=\"selectedBillForWorkflow\"\n (workflowCompleted)=\"onWorkflowCompleted($event)\">\n </sdk-bill-workflow>\n \n \n}@else { \n <div class=\"content\">\n <header class=\"page-header\">\n <div class=\"d-flex align-items-center flex-wrap\">\n <div class=\"mr-auto\">\n <h1>{{ 'LABEL_UTILITIES' | translate }}</h1>\n </div>\n </div>\n </header>\n \n <section class=\"page-content container-fluid\">\n \n <div class=\"card m-0 border-0\">\n <div class=\"card-header p-t-25 p-b-20\">\n <h4 class=\"card-title font-weight-600 font-size-18 m-0 text-dark\">{{ vendorProvider?.provider_name }}\n </h4>\n </div>\n <div class=\"tab-content\">\n <div class=\"tab-pane fadeIn active show\">\n <form role=\"form\" class=\"form-horizontal\" [formGroup]=\"detailsForm\" (ngSubmit)=\"submitForm()\">\n <div class=\"card-body\">\n <div class=\"mt-3\">\n @if (fieldsArray.length > 0) {\n <div class=\"row\">\n <div class=\"col-xl-5 offset-xl-3\">\n <!-- <div class=\"form-group form-row\">\n <label\n class=\"control-label text-md-right col-md-4 col-lg-3\">Provider</label>\n <div class=\"col-md-5\">\n <ng-select placeholder=\"Select Provider\" bindLabel=\"provider_name\"\n bindValue=\"id\" [items]=\"vendorProviders\"\n (change)=\"changeVendorProvider($event)\">\n </ng-select>\n </div>\n </div> -->\n <!-- <pre>{{fieldsArray|json}}</pre> -->\n <formly-form [fields]=\"fieldsArray\" [model]=\"fieldsModel\" [form]=\"detailsForm\">\n </formly-form>\n </div>\n </div>\n }\n \n @if (fieldsArray.length === 0) {\n <sdk-no-data title=\"LABEL_FIELDS_NOT_AVAILABLE\"></sdk-no-data>\n }\n </div>\n </div>\n <div class=\"card-footer bg-light text-right\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"back()\">\n {{'COMMON_LABEL_CANCEL' | translate}}\n </button>\n @if (buttonData) {\n <button type=\"submit\" class=\"btn btn-primary\"\n [ngClass]=\"{'qt-loader qt-loader-mini qt-loader-left': showLoader}\"\n [disabled]=\"showLoader || isFormIncomplete()\">\n {{ buttonData?.field_title }}\n </button>\n }\n </div>\n </form>\n </div>\n </div>\n </div>\n </section>\n </div>\n}", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: SharedModule }, { kind: "ngmodule", type: NgSelectModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormlyBootstrapModule }, { kind: "ngmodule", type: FormlyModule }, { kind: "component", type: i1$2.LegacyFormlyForm, selector: "formly-form" }, { kind: "component", type: NoDataComponent, selector: "sdk-no-data", inputs: ["title", "icon"] }, { kind: "component", type: BillWorkflowComponent, selector: "sdk-bill-workflow", inputs: ["billData", "selectedBill"], outputs: ["workflowCompleted"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }] });
|
|
3120
3127
|
}
|
|
3121
3128
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: WorkFlowComponent, decorators: [{
|
|
3122
3129
|
type: Component,
|
|
@@ -3166,7 +3173,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3166
3173
|
},
|
|
3167
3174
|
],
|
|
3168
3175
|
}),
|
|
3169
|
-
], template: "\n\n@if (showWorkflowComponent) {\n \n <sdk-bill-workflow \n [selectedBill]=\"selectedBillForWorkflow\"\n (workflowCompleted)=\"onWorkflowCompleted($event)\">\n </sdk-bill-workflow>\n \n \n}@else { \n <div class=\"content\">\n <header class=\"page-header\">\n <div class=\"d-flex align-items-center flex-wrap\">\n <div class=\"mr-auto\">\n <h1>{{ 'LABEL_UTILITIES' | translate }}</h1>\n </div>\n </div>\n </header>\n \n <section class=\"page-content container-fluid\">\n \n <div class=\"card m-0 border-0\">\n <div class=\"card-header p-t-25 p-b-20\">\n <h4 class=\"card-title font-weight-600 font-size-18 m-0 text-dark\">{{ vendorProvider?.provider_name }}\n </h4>\n </div>\n <div class=\"tab-content\">\n <div class=\"tab-pane fadeIn active show\">\n <form role=\"form\" class=\"form-horizontal\" [formGroup]=\"detailsForm\" (ngSubmit)=\"submitForm()\">\n <div class=\"card-body\">\n <div class=\"mt-3\">\n @if (fieldsArray.length > 0) {\n <div class=\"row\">\n <div class=\"col-xl-5 offset-xl-3\">\n <!-- <div class=\"form-group form-row\">\n <label\n class=\"control-label text-md-right col-md-4 col-lg-3\">Provider</label>\n <div class=\"col-md-5\">\n <ng-select placeholder=\"Select Provider\" bindLabel=\"provider_name\"\n bindValue=\"id\" [items]=\"vendorProviders\"\n (change)=\"changeVendorProvider($event)\">\n </ng-select>\n </div>\n </div> -->\n <!-- <pre>{{fieldsArray|json}}</pre> -->\n <formly-form [fields]=\"fieldsArray\" [model]=\"fieldsModel\" [form]=\"detailsForm\">\n </formly-form>\n </div>\n </div>\n }\n \n @if (fieldsArray.length === 0) {\n <sdk-no-data title=\"LABEL_FIELDS_NOT_AVAILABLE\"></sdk-no-data>\n }\n </div>\n </div>\n <div class=\"card-footer bg-light text-right\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"back()\">\n {{'COMMON_LABEL_CANCEL' | translate}}\n </button>\n @if (buttonData) {\n <button type=\"submit\" class=\"btn btn-primary\"\n [ngClass]=\"{'qt-loader qt-loader-mini qt-loader-left': showLoader}\"\n [disabled]=\"showLoader\">\n {{ buttonData?.field_title }}\n </button>\n }\n </div>\n </form>\n </div>\n </div>\n </div>\n </section>\n </div>\n}" }]
|
|
3176
|
+
], template: "\n\n@if (showWorkflowComponent) {\n \n <sdk-bill-workflow \n [selectedBill]=\"selectedBillForWorkflow\"\n (workflowCompleted)=\"onWorkflowCompleted($event)\">\n </sdk-bill-workflow>\n \n \n}@else { \n <div class=\"content\">\n <header class=\"page-header\">\n <div class=\"d-flex align-items-center flex-wrap\">\n <div class=\"mr-auto\">\n <h1>{{ 'LABEL_UTILITIES' | translate }}</h1>\n </div>\n </div>\n </header>\n \n <section class=\"page-content container-fluid\">\n \n <div class=\"card m-0 border-0\">\n <div class=\"card-header p-t-25 p-b-20\">\n <h4 class=\"card-title font-weight-600 font-size-18 m-0 text-dark\">{{ vendorProvider?.provider_name }}\n </h4>\n </div>\n <div class=\"tab-content\">\n <div class=\"tab-pane fadeIn active show\">\n <form role=\"form\" class=\"form-horizontal\" [formGroup]=\"detailsForm\" (ngSubmit)=\"submitForm()\">\n <div class=\"card-body\">\n <div class=\"mt-3\">\n @if (fieldsArray.length > 0) {\n <div class=\"row\">\n <div class=\"col-xl-5 offset-xl-3\">\n <!-- <div class=\"form-group form-row\">\n <label\n class=\"control-label text-md-right col-md-4 col-lg-3\">Provider</label>\n <div class=\"col-md-5\">\n <ng-select placeholder=\"Select Provider\" bindLabel=\"provider_name\"\n bindValue=\"id\" [items]=\"vendorProviders\"\n (change)=\"changeVendorProvider($event)\">\n </ng-select>\n </div>\n </div> -->\n <!-- <pre>{{fieldsArray|json}}</pre> -->\n <formly-form [fields]=\"fieldsArray\" [model]=\"fieldsModel\" [form]=\"detailsForm\">\n </formly-form>\n </div>\n </div>\n }\n \n @if (fieldsArray.length === 0) {\n <sdk-no-data title=\"LABEL_FIELDS_NOT_AVAILABLE\"></sdk-no-data>\n }\n </div>\n </div>\n <div class=\"card-footer bg-light text-right\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"back()\">\n {{'COMMON_LABEL_CANCEL' | translate}}\n </button>\n @if (buttonData) {\n <button type=\"submit\" class=\"btn btn-primary\"\n [ngClass]=\"{'qt-loader qt-loader-mini qt-loader-left': showLoader}\"\n [disabled]=\"showLoader || isFormIncomplete()\">\n {{ buttonData?.field_title }}\n </button>\n }\n </div>\n </form>\n </div>\n </div>\n </div>\n </section>\n </div>\n}" }]
|
|
3170
3177
|
}], ctorParameters: () => [] });
|
|
3171
3178
|
|
|
3172
3179
|
const REGION_COMMUNE_DATA = {
|
|
@@ -3775,7 +3782,6 @@ class ViewBillsComponent {
|
|
|
3775
3782
|
this.topupAndBillpaymentService.workFlowUserInput.pipe(takeUntil(this.destroy$)).subscribe((data) => {
|
|
3776
3783
|
if (isNotNull(data)) {
|
|
3777
3784
|
this.workFlowUserInput = data;
|
|
3778
|
-
console.log("workFlowUserInput==>", this.workFlowUserInput);
|
|
3779
3785
|
if (data?.length > 1) {
|
|
3780
3786
|
const latestNumericValue = data?.find((element) => element['value'] && typeof element['value'] === 'number')?.value || null;
|
|
3781
3787
|
this.userAmount = latestNumericValue;
|
|
@@ -3835,6 +3841,7 @@ class ViewBillsComponent {
|
|
|
3835
3841
|
this.showLoader = false;
|
|
3836
3842
|
if (res?.success) {
|
|
3837
3843
|
this.billsData = res?.data;
|
|
3844
|
+
this.showLoader = false;
|
|
3838
3845
|
}
|
|
3839
3846
|
else {
|
|
3840
3847
|
this.toasterService.error(this.translationService.translate(res?.error[0]));
|
|
@@ -5906,6 +5913,7 @@ class TopupAndBillpaymentReviewComponent extends TransactionClass {
|
|
|
5906
5913
|
this.back();
|
|
5907
5914
|
}
|
|
5908
5915
|
}).catch(() => undefined);
|
|
5916
|
+
this.topupAndBillpaymentService?.deleteWorkflowUserInput();
|
|
5909
5917
|
}
|
|
5910
5918
|
removeunderScore(value) {
|
|
5911
5919
|
return value.replace(/_/g, ' ').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "digipay-utility-payment",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@angular/common": "^20.
|
|
6
|
-
"@angular/core": "^20.
|
|
5
|
+
"@angular/common": "^20.0.0",
|
|
6
|
+
"@angular/core": "^20.0.0",
|
|
7
|
+
"@ng-select/ng-select": "^15.1.3",
|
|
8
|
+
"@ngx-formly/bootstrap": "^7.0.0",
|
|
9
|
+
"@ngx-formly/core": "^7.0.0",
|
|
10
|
+
"ngx-toastr": "^19.0.0",
|
|
11
|
+
"lodash": "^4.17.21",
|
|
12
|
+
"rxjs": "^7.8.0"
|
|
7
13
|
},
|
|
8
14
|
"dependencies": {
|
|
9
15
|
"@popperjs/core": "^2.11.8",
|