http-request-manager 18.11.22 → 18.12.1

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.
@@ -2655,6 +2655,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2655
2655
 
2656
2656
  // ChannelType is already exported from request-manager-state-service to avoid conflicts
2657
2657
 
2658
+ class DatabaseStorage {
2659
+ constructor(table = '', expiresIn) {
2660
+ this.table = table;
2661
+ this.expiresIn = expiresIn;
2662
+ }
2663
+ static adapt(item) {
2664
+ if (!item?.table)
2665
+ return;
2666
+ return new DatabaseStorage(item?.table, item?.expiresIn);
2667
+ }
2668
+ }
2669
+
2670
+ class RetryOptions {
2671
+ constructor(times = 0, delay = 3) {
2672
+ this.times = times;
2673
+ this.delay = delay;
2674
+ }
2675
+ static adapt(item) {
2676
+ return new RetryOptions(item?.times, item?.delay);
2677
+ }
2678
+ }
2679
+
2680
+ class BatchOptions {
2681
+ constructor(mode = 'sequential', stopOnError = false, concurrency = 3, ignoreErrors = false, logErrors = true, displaySuccess, displayError) {
2682
+ this.mode = mode;
2683
+ this.stopOnError = stopOnError;
2684
+ this.concurrency = concurrency;
2685
+ this.ignoreErrors = ignoreErrors;
2686
+ this.logErrors = logErrors;
2687
+ this.displaySuccess = displaySuccess;
2688
+ this.displayError = displayError;
2689
+ }
2690
+ static adapt(item) {
2691
+ return new BatchOptions(item?.mode ?? 'sequential', item?.stopOnError ?? false, item?.concurrency ?? 3, item?.ignoreErrors ?? false, item?.logErrors !== false, item?.displaySuccess ?? false, item?.displayError ?? false);
2692
+ }
2693
+ }
2694
+
2695
+ class BatchResult {
2696
+ constructor(request, success, data, error, index = 0, timestamp = Date.now()) {
2697
+ this.request = request;
2698
+ this.success = success;
2699
+ this.data = data;
2700
+ this.error = error;
2701
+ this.index = index;
2702
+ this.timestamp = timestamp;
2703
+ }
2704
+ static adapt(item) {
2705
+ return new BatchResult(item?.request, item?.success ?? false, item?.data, item?.error, item?.index ?? 0, item?.timestamp ?? Date.now());
2706
+ }
2707
+ }
2708
+
2709
+ function isPendingState(state) {
2710
+ return state.isPending === true;
2711
+ }
2712
+ function isSuccessState(state) {
2713
+ return state.isPending === false && state.data !== undefined;
2714
+ }
2715
+ function isErrorState(state) {
2716
+ return state.isPending === false && state.error !== undefined;
2717
+ }
2718
+
2719
+ function calculateBatchProgress(states) {
2720
+ const total = states.length;
2721
+ const pending = states.filter(s => s.isPending).length;
2722
+ const completed = states.filter(s => !s.isPending && s.data !== undefined).length;
2723
+ const failed = states.filter(s => !s.isPending && s.error !== undefined).length;
2724
+ const percent = total > 0 ? Math.round(((completed + failed) / total) * 100) : 0;
2725
+ return { total, pending, completed, failed, percent };
2726
+ }
2727
+
2728
+ var DataType;
2729
+ (function (DataType) {
2730
+ DataType[DataType["ANY"] = 0] = "ANY";
2731
+ DataType[DataType["ARRAY"] = 1] = "ARRAY";
2732
+ DataType[DataType["OBJECT"] = 2] = "OBJECT";
2733
+ })(DataType || (DataType = {}));
2734
+
2735
+ class ConfigHTTPOptions {
2736
+ constructor(server = '', path, headers, polling, retry, stream, displayError, displaySuccess) {
2737
+ this.server = server;
2738
+ this.path = path;
2739
+ this.headers = headers;
2740
+ this.polling = polling;
2741
+ this.retry = retry;
2742
+ this.stream = stream;
2743
+ this.displayError = displayError;
2744
+ this.displaySuccess = displaySuccess;
2745
+ }
2746
+ static adapt(item) {
2747
+ const server = Array.isArray(item?.server) ? item.server.join('/') : item?.server || '';
2748
+ const retryOptions = item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt();
2749
+ return new ConfigHTTPOptions(server, (item?.path) ? item.path : [], (item?.headers) ? item.headers : {}, item?.polling ? Math.floor(item.polling) : 0, retryOptions, (item?.stream) ? item.stream : false, (item?.displayError) ? item.displayError : false, (item?.displaySuccess) ? item.displaySuccess : false);
2750
+ }
2751
+ }
2752
+
2753
+ class LocalStorageOptions {
2754
+ constructor(storageName, storageSettingsName, options) {
2755
+ this.storageName = storageName;
2756
+ this.storageSettingsName = storageSettingsName;
2757
+ this.options = options;
2758
+ }
2759
+ static adapt(item) {
2760
+ return new LocalStorageOptions((item?.storageName) ? item.storageName : 'storage', (item?.storageSettingsName) ? item.storageSettingsName : 'global-storage', (item?.options) ? SettingOptions.adapt(item.options) : SettingOptions.adapt());
2761
+ }
2762
+ }
2763
+
2764
+ class ConfigOptions {
2765
+ constructor(httpRequestOptions, LocalStorageOptions) {
2766
+ this.httpRequestOptions = httpRequestOptions;
2767
+ this.LocalStorageOptions = LocalStorageOptions;
2768
+ }
2769
+ static adapt(item) {
2770
+ return new ConfigOptions((item?.httpRequestOptions) ? ConfigHTTPOptions.adapt(item.httpRequestOptions) : undefined, (item?.LocalStorageOptions) ? LocalStorageOptions.adapt(item.LocalStorageOptions) : undefined);
2771
+ }
2772
+ }
2773
+
2774
+ class InvalidFileInfoModel {
2775
+ constructor(fileName = '', fileSize = 0, fileType = '', errors = []) {
2776
+ this.fileName = fileName;
2777
+ this.fileSize = fileSize;
2778
+ this.fileType = fileType;
2779
+ this.errors = errors;
2780
+ }
2781
+ static adapt(item) {
2782
+ return new InvalidFileInfoModel(item?.fileName, item?.fileSize, item?.fileType, Array.isArray(item?.errors) ? item.errors : []);
2783
+ }
2784
+ }
2785
+ class UploadValidationErrorModel {
2786
+ constructor(invalidFiles = [], validFilesCount = 0, totalFilesCount = 0) {
2787
+ this.invalidFiles = invalidFiles;
2788
+ this.validFilesCount = validFilesCount;
2789
+ this.totalFilesCount = totalFilesCount;
2790
+ }
2791
+ static adapt(item) {
2792
+ return new UploadValidationErrorModel(Array.isArray(item?.invalidFiles) ? item.invalidFiles.map((f) => InvalidFileInfoModel.adapt(f)) : [], item?.validFilesCount, item?.totalFilesCount);
2793
+ }
2794
+ }
2795
+
2796
+ class UserData {
2797
+ constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
2798
+ this.ldap = ldap;
2799
+ this.name = name;
2800
+ this.email = email;
2801
+ this.color = color;
2802
+ }
2803
+ static adapt(item) {
2804
+ const userName = `${item?.first_name} ${item?.last_name}`;
2805
+ return new UserData(item?.ldap, userName, item?.email, item?.color);
2806
+ }
2807
+ }
2808
+
2658
2809
  class RequestService extends WebsocketService {
2659
2810
  constructor() {
2660
2811
  super(...arguments);
@@ -2795,6 +2946,89 @@ class RequestService extends WebsocketService {
2795
2946
  return throwError(() => err);
2796
2947
  }));
2797
2948
  }
2949
+ uploadFileRequest(options) {
2950
+ this.isPending.next(true);
2951
+ const files = options.uploadFiles ? (Array.isArray(options.uploadFiles) ? options.uploadFiles : [options.uploadFiles]) : [];
2952
+ const validation = this.validateUploadFiles(files, options);
2953
+ if (validation.validFiles.length === 0) {
2954
+ this.isPending.next(false);
2955
+ return throwError(() => new UploadValidationErrorModel(validation.invalidFiles, 0, files.length));
2956
+ }
2957
+ const fieldName = options.uploadFieldName || (files.length === 1 ? 'file' : 'files');
2958
+ const formData = this.buildFormData(validation.validFiles, fieldName);
2959
+ const urlPath = this.buildUrlPath(options);
2960
+ const method = options.uploadHttpMethod || 'POST';
2961
+ return this.http.request(method, urlPath, {
2962
+ body: formData,
2963
+ observe: 'events',
2964
+ reportProgress: true
2965
+ }).pipe(map((event) => {
2966
+ this.isPending.next(true);
2967
+ switch (event.type) {
2968
+ case HttpEventType.UploadProgress:
2969
+ const status = event.total ? Math.round(event.loaded / (event.total || 1) * 100) : 0;
2970
+ this.progress.next(status);
2971
+ return null;
2972
+ case HttpEventType.Response:
2973
+ this.isPending.next(false);
2974
+ this.progress.next(0);
2975
+ return event.body;
2976
+ default:
2977
+ return null;
2978
+ }
2979
+ }), filter((body) => body !== null), catchError(err => {
2980
+ this.isPending.next(false);
2981
+ this.progress.next(0);
2982
+ return throwError(() => err);
2983
+ }));
2984
+ }
2985
+ validateUploadFiles(files, options) {
2986
+ const validFiles = [];
2987
+ const invalidFiles = [];
2988
+ let totalSize = 0;
2989
+ for (const file of files) {
2990
+ const errors = [];
2991
+ if (options.allowedTypes && options.allowedTypes.length > 0) {
2992
+ const isAllowed = options.allowedTypes.some(type => {
2993
+ if (type.endsWith('/*')) {
2994
+ return file.type.startsWith(type.replace('/*', '/'));
2995
+ }
2996
+ return file.type === type;
2997
+ });
2998
+ if (!isAllowed) {
2999
+ errors.push('type not allowed');
3000
+ }
3001
+ }
3002
+ const maxFileSize = options.maxFileSize !== undefined ? options.maxFileSize : Infinity;
3003
+ if (file.size > maxFileSize) {
3004
+ errors.push('exceeds maxFileSize');
3005
+ }
3006
+ if (errors.length > 0) {
3007
+ invalidFiles.push(new InvalidFileInfoModel(file.name, file.size, file.type, errors));
3008
+ }
3009
+ else {
3010
+ validFiles.push(file);
3011
+ totalSize += file.size;
3012
+ }
3013
+ }
3014
+ const maxTotalSize = options.maxTotalSize !== undefined ? options.maxTotalSize : Infinity;
3015
+ if (totalSize > maxTotalSize) {
3016
+ return {
3017
+ validFiles: [],
3018
+ invalidFiles: [
3019
+ new InvalidFileInfoModel('total', totalSize, '', ['exceeds maxTotalSize'])
3020
+ ]
3021
+ };
3022
+ }
3023
+ return { validFiles, invalidFiles };
3024
+ }
3025
+ buildFormData(files, fieldName) {
3026
+ const formData = new FormData();
3027
+ files.forEach((file) => {
3028
+ formData.append(fieldName, file);
3029
+ });
3030
+ return formData;
3031
+ }
2798
3032
  handleFinalize() {
2799
3033
  return finalize(() => this.isPending.next(false));
2800
3034
  }
@@ -2912,136 +3146,6 @@ function requestPolling(pollInterval, stopCondition$, isPending$) {
2912
3146
  };
2913
3147
  }
2914
3148
 
2915
- class DatabaseStorage {
2916
- constructor(table = '', expiresIn) {
2917
- this.table = table;
2918
- this.expiresIn = expiresIn;
2919
- }
2920
- static adapt(item) {
2921
- if (!item?.table)
2922
- return;
2923
- return new DatabaseStorage(item?.table, item?.expiresIn);
2924
- }
2925
- }
2926
-
2927
- class RetryOptions {
2928
- constructor(times = 0, delay = 3) {
2929
- this.times = times;
2930
- this.delay = delay;
2931
- }
2932
- static adapt(item) {
2933
- return new RetryOptions(item?.times, item?.delay);
2934
- }
2935
- }
2936
-
2937
- class BatchOptions {
2938
- constructor(mode = 'sequential', stopOnError = false, concurrency = 3, ignoreErrors = false, logErrors = true, displaySuccess, successMessage) {
2939
- this.mode = mode;
2940
- this.stopOnError = stopOnError;
2941
- this.concurrency = concurrency;
2942
- this.ignoreErrors = ignoreErrors;
2943
- this.logErrors = logErrors;
2944
- this.displaySuccess = displaySuccess;
2945
- this.successMessage = successMessage;
2946
- }
2947
- static adapt(item) {
2948
- return new BatchOptions(item?.mode ?? 'sequential', item?.stopOnError ?? false, item?.concurrency ?? 3, item?.ignoreErrors ?? false, item?.logErrors !== false, item?.displaySuccess ?? false, item?.successMessage);
2949
- }
2950
- }
2951
-
2952
- class BatchResult {
2953
- constructor(request, success, data, error, index = 0, timestamp = Date.now()) {
2954
- this.request = request;
2955
- this.success = success;
2956
- this.data = data;
2957
- this.error = error;
2958
- this.index = index;
2959
- this.timestamp = timestamp;
2960
- }
2961
- static adapt(item) {
2962
- return new BatchResult(item?.request, item?.success ?? false, item?.data, item?.error, item?.index ?? 0, item?.timestamp ?? Date.now());
2963
- }
2964
- }
2965
-
2966
- function isPendingState(state) {
2967
- return state.isPending === true;
2968
- }
2969
- function isSuccessState(state) {
2970
- return state.isPending === false && state.data !== undefined;
2971
- }
2972
- function isErrorState(state) {
2973
- return state.isPending === false && state.error !== undefined;
2974
- }
2975
-
2976
- function calculateBatchProgress(states) {
2977
- const total = states.length;
2978
- const pending = states.filter(s => s.isPending).length;
2979
- const completed = states.filter(s => !s.isPending && s.data !== undefined).length;
2980
- const failed = states.filter(s => !s.isPending && s.error !== undefined).length;
2981
- const percent = total > 0 ? Math.round(((completed + failed) / total) * 100) : 0;
2982
- return { total, pending, completed, failed, percent };
2983
- }
2984
-
2985
- var DataType;
2986
- (function (DataType) {
2987
- DataType[DataType["ANY"] = 0] = "ANY";
2988
- DataType[DataType["ARRAY"] = 1] = "ARRAY";
2989
- DataType[DataType["OBJECT"] = 2] = "OBJECT";
2990
- })(DataType || (DataType = {}));
2991
-
2992
- class ConfigHTTPOptions {
2993
- constructor(server = '', path, headers, polling, retry, stream, displayError, displaySuccess, successMessage) {
2994
- this.server = server;
2995
- this.path = path;
2996
- this.headers = headers;
2997
- this.polling = polling;
2998
- this.retry = retry;
2999
- this.stream = stream;
3000
- this.displayError = displayError;
3001
- this.displaySuccess = displaySuccess;
3002
- this.successMessage = successMessage;
3003
- }
3004
- static adapt(item) {
3005
- const server = Array.isArray(item?.server) ? item.server.join('/') : item?.server || '';
3006
- const retryOptions = item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt();
3007
- return new ConfigHTTPOptions(server, (item?.path) ? item.path : [], (item?.headers) ? item.headers : {}, item?.polling ? Math.floor(item.polling) : 0, retryOptions, (item?.stream) ? item.stream : false, (item?.displayError) ? item.displayError : false, (item?.displaySuccess) ? item.displaySuccess : false, item?.successMessage);
3008
- }
3009
- }
3010
-
3011
- class LocalStorageOptions {
3012
- constructor(storageName, storageSettingsName, options) {
3013
- this.storageName = storageName;
3014
- this.storageSettingsName = storageSettingsName;
3015
- this.options = options;
3016
- }
3017
- static adapt(item) {
3018
- return new LocalStorageOptions((item?.storageName) ? item.storageName : 'storage', (item?.storageSettingsName) ? item.storageSettingsName : 'global-storage', (item?.options) ? SettingOptions.adapt(item.options) : SettingOptions.adapt());
3019
- }
3020
- }
3021
-
3022
- class ConfigOptions {
3023
- constructor(httpRequestOptions, LocalStorageOptions) {
3024
- this.httpRequestOptions = httpRequestOptions;
3025
- this.LocalStorageOptions = LocalStorageOptions;
3026
- }
3027
- static adapt(item) {
3028
- return new ConfigOptions((item?.httpRequestOptions) ? ConfigHTTPOptions.adapt(item.httpRequestOptions) : undefined, (item?.LocalStorageOptions) ? LocalStorageOptions.adapt(item.LocalStorageOptions) : undefined);
3029
- }
3030
- }
3031
-
3032
- class UserData {
3033
- constructor(ldap = '', name = '', email = '', color = RandomPaletteColor()) {
3034
- this.ldap = ldap;
3035
- this.name = name;
3036
- this.email = email;
3037
- this.color = color;
3038
- }
3039
- static adapt(item) {
3040
- const userName = `${item?.first_name} ${item?.last_name}`;
3041
- return new UserData(item?.ldap, userName, item?.email, item?.color);
3042
- }
3043
- }
3044
-
3045
3149
  /**
3046
3150
  * Message Tracker Service - Guaranteed Message Delivery
3047
3151
  *
@@ -3628,7 +3732,7 @@ class HTTPManagerService extends RequestService {
3628
3732
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3629
3733
  })).pipe(finalize(() => this.isPending.next(false)), catchError((err) => {
3630
3734
  if (updatedOptions.displayError)
3631
- this.handleErrorWithSnackBar(err);
3735
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
3632
3736
  this.isPending.next(false);
3633
3737
  return this.handleError(err);
3634
3738
  }));
@@ -3645,7 +3749,7 @@ class HTTPManagerService extends RequestService {
3645
3749
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3646
3750
  }), finalize(() => this.isPending.next(false)), catchError((err) => {
3647
3751
  if (updatedOptions.displayError)
3648
- this.handleErrorWithSnackBar(err);
3752
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
3649
3753
  this.isPending.next(false);
3650
3754
  return this.handleError(err);
3651
3755
  }));
@@ -3662,7 +3766,7 @@ class HTTPManagerService extends RequestService {
3662
3766
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3663
3767
  }), finalize(() => this.isPending.next(false)), catchError((err) => {
3664
3768
  if (updatedOptions.displayError)
3665
- this.handleErrorWithSnackBar(err);
3769
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
3666
3770
  this.isPending.next(false);
3667
3771
  return this.handleError(err);
3668
3772
  }));
@@ -3679,7 +3783,7 @@ class HTTPManagerService extends RequestService {
3679
3783
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3680
3784
  }), finalize(() => this.isPending.next(false)), catchError((err) => {
3681
3785
  if (updatedOptions.displayError)
3682
- this.handleErrorWithSnackBar(err);
3786
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
3683
3787
  this.isPending.next(false);
3684
3788
  return this.handleError(err);
3685
3789
  }));
@@ -3696,6 +3800,24 @@ class HTTPManagerService extends RequestService {
3696
3800
  return this.handleError(err);
3697
3801
  }));
3698
3802
  }
3803
+ uploadRequest(files, options, params) {
3804
+ this.isPending.next(true);
3805
+ this.data.next(null);
3806
+ const updatedOptions = this.defineReqOptions(options, params);
3807
+ updatedOptions.uploadFiles = files;
3808
+ const func = this.uploadFileRequest;
3809
+ const requests = this.createRequest(func, updatedOptions);
3810
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => {
3811
+ this.data.next(data);
3812
+ if (updatedOptions.displaySuccess)
3813
+ this.handleSuccessWithSnackBar(updatedOptions.successMessage);
3814
+ }), finalize(() => this.isPending.next(false)), catchError((err) => {
3815
+ if (updatedOptions.displayError)
3816
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
3817
+ this.isPending.next(false);
3818
+ return this.handleError(err);
3819
+ }));
3820
+ }
3699
3821
  createObservable(options, request$, funcName) {
3700
3822
  const polling = options.polling ? (options.polling > 0 ? true : false) : false;
3701
3823
  const isPolling = polling &&
@@ -3768,9 +3890,9 @@ class HTTPManagerService extends RequestService {
3768
3890
  this.error.next(error.message || `${error.status} - ${error.statusText}`);
3769
3891
  return throwError(() => error);
3770
3892
  }
3771
- handleErrorWithSnackBar(error) {
3893
+ handleErrorWithSnackBar(error, message) {
3772
3894
  const displayError = ToastDisplay.adapt({
3773
- message: error.message || `${error.status} - ${error.statusText}`,
3895
+ message: message || error.message || `${error.status} - ${error.statusText}`,
3774
3896
  action: 'OK',
3775
3897
  color: ToastColors.ERROR,
3776
3898
  icon: 'error',
@@ -3780,7 +3902,7 @@ class HTTPManagerService extends RequestService {
3780
3902
  }
3781
3903
  handleSuccessWithSnackBar(message) {
3782
3904
  const displaySuccess = ToastDisplay.adapt({
3783
- message: message || 'OK',
3905
+ message: message || 'Success',
3784
3906
  action: 'OK',
3785
3907
  color: ToastColors.SUCCESS,
3786
3908
  icon: 'check_circle',
@@ -3823,16 +3945,16 @@ class HTTPManagerService extends RequestService {
3823
3945
  * @param options Optional batch configuration
3824
3946
  * @returns Observable emitting array of data when all requests complete
3825
3947
  */
3826
- getSequentialRequest(requests, options) {
3948
+ getSequentialRequest(requests, options, successMessage, errorMessage) {
3827
3949
  const batchOptions = BatchOptions.adapt(options);
3828
3950
  const results = new Array(requests.length);
3829
3951
  return from(requests).pipe(concatMap((req, index) => {
3830
- return this.getRequest(req).pipe(tap(data => {
3952
+ return this.getRequest(req, undefined).pipe(tap(data => {
3831
3953
  results[index] = data;
3832
3954
  }), catchError(error => this.handleSequentialError(req, error, index, batchOptions)));
3833
3955
  }), toArray(), tap(() => {
3834
3956
  if (batchOptions.displaySuccess)
3835
- this.handleSuccessWithSnackBar(batchOptions.successMessage);
3957
+ this.handleSuccessWithSnackBar(successMessage);
3836
3958
  }), map(() => results));
3837
3959
  }
3838
3960
  /**
@@ -3841,17 +3963,17 @@ class HTTPManagerService extends RequestService {
3841
3963
  * @param options Optional batch configuration with concurrency limit
3842
3964
  * @returns Observable emitting array of data when all requests complete
3843
3965
  */
3844
- getParallelRequest(requests, options) {
3966
+ getParallelRequest(requests, options, successMessage, errorMessage) {
3845
3967
  const batchOptions = BatchOptions.adapt(options);
3846
3968
  const concurrency = batchOptions.concurrency || 3;
3847
3969
  const results = new Array(requests.length);
3848
3970
  return from(requests).pipe(mergeMap((req, index) => {
3849
- return this.getRequest(req).pipe(tap(data => {
3971
+ return this.getRequest(req, undefined).pipe(tap(data => {
3850
3972
  results[index] = data;
3851
3973
  }), catchError(error => this.handleParallelError(req, error, index, batchOptions)));
3852
3974
  }, concurrency), toArray(), tap(() => {
3853
3975
  if (batchOptions.displaySuccess)
3854
- this.handleSuccessWithSnackBar(batchOptions.successMessage);
3976
+ this.handleSuccessWithSnackBar(successMessage);
3855
3977
  }), map(() => results));
3856
3978
  }
3857
3979
  /**
@@ -4085,6 +4207,89 @@ class RequestSignalsService extends WebsocketService {
4085
4207
  return throwError(() => err);
4086
4208
  }));
4087
4209
  }
4210
+ uploadFileRequest(options) {
4211
+ this.isPending.set(true);
4212
+ const files = options.uploadFiles ? (Array.isArray(options.uploadFiles) ? options.uploadFiles : [options.uploadFiles]) : [];
4213
+ const validation = this.validateUploadFiles(files, options);
4214
+ if (validation.validFiles.length === 0) {
4215
+ this.isPending.set(false);
4216
+ return throwError(() => new UploadValidationErrorModel(validation.invalidFiles, 0, files.length));
4217
+ }
4218
+ const fieldName = options.uploadFieldName || (files.length === 1 ? 'file' : 'files');
4219
+ const formData = this.buildFormData(validation.validFiles, fieldName);
4220
+ const urlPath = this.buildUrlPath(options);
4221
+ const method = options.uploadHttpMethod || 'POST';
4222
+ return this.http.request(method, urlPath, {
4223
+ body: formData,
4224
+ observe: 'events',
4225
+ reportProgress: true
4226
+ }).pipe(map((event) => {
4227
+ this.isPending.set(true);
4228
+ switch (event.type) {
4229
+ case HttpEventType.UploadProgress:
4230
+ const status = event.total ? Math.round(event.loaded / (event.total || 1) * 100) : 0;
4231
+ this.progress.set(status);
4232
+ return null;
4233
+ case HttpEventType.Response:
4234
+ this.isPending.set(false);
4235
+ this.progress.set(0);
4236
+ return event.body;
4237
+ default:
4238
+ return null;
4239
+ }
4240
+ }), filter((body) => body !== null), catchError(err => {
4241
+ this.isPending.set(false);
4242
+ this.progress.set(0);
4243
+ return throwError(() => err);
4244
+ }));
4245
+ }
4246
+ validateUploadFiles(files, options) {
4247
+ const validFiles = [];
4248
+ const invalidFiles = [];
4249
+ let totalSize = 0;
4250
+ for (const file of files) {
4251
+ const errors = [];
4252
+ if (options.allowedTypes && options.allowedTypes.length > 0) {
4253
+ const isAllowed = options.allowedTypes.some(type => {
4254
+ if (type.endsWith('/*')) {
4255
+ return file.type.startsWith(type.replace('/*', '/'));
4256
+ }
4257
+ return file.type === type;
4258
+ });
4259
+ if (!isAllowed) {
4260
+ errors.push('type not allowed');
4261
+ }
4262
+ }
4263
+ const maxFileSize = options.maxFileSize !== undefined ? options.maxFileSize : Infinity;
4264
+ if (file.size > maxFileSize) {
4265
+ errors.push('exceeds maxFileSize');
4266
+ }
4267
+ if (errors.length > 0) {
4268
+ invalidFiles.push(new InvalidFileInfoModel(file.name, file.size, file.type, errors));
4269
+ }
4270
+ else {
4271
+ validFiles.push(file);
4272
+ totalSize += file.size;
4273
+ }
4274
+ }
4275
+ const maxTotalSize = options.maxTotalSize !== undefined ? options.maxTotalSize : Infinity;
4276
+ if (totalSize > maxTotalSize) {
4277
+ return {
4278
+ validFiles: [],
4279
+ invalidFiles: [
4280
+ new InvalidFileInfoModel('total', totalSize, '', ['exceeds maxTotalSize'])
4281
+ ]
4282
+ };
4283
+ }
4284
+ return { validFiles, invalidFiles };
4285
+ }
4286
+ buildFormData(files, fieldName) {
4287
+ const formData = new FormData();
4288
+ files.forEach((file, index) => {
4289
+ formData.append(`${fieldName}[${index}]`, file);
4290
+ });
4291
+ return formData;
4292
+ }
4088
4293
  handleFinalize() {
4089
4294
  return finalize(() => this.isPending.set(false));
4090
4295
  }
@@ -4312,7 +4517,7 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4312
4517
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4313
4518
  }), finalize(() => this.isPending.set(false)), catchError((err) => {
4314
4519
  if (updatedOptions.displayError)
4315
- this.handleErrorWithSnackBar(err);
4520
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
4316
4521
  this.isPending.set(false);
4317
4522
  return this.handleError(err);
4318
4523
  }));
@@ -4329,7 +4534,7 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4329
4534
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4330
4535
  }), finalize(() => this.isPending.set(false)), catchError((err) => {
4331
4536
  if (updatedOptions.displayError)
4332
- this.handleErrorWithSnackBar(err);
4537
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage);
4333
4538
  this.isPending.set(false);
4334
4539
  return this.handleError(err);
4335
4540
  }));
@@ -4346,7 +4551,7 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4346
4551
  this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4347
4552
  }), finalize(() => this.isPending.set(false)), catchError((err) => {
4348
4553
  if (updatedOptions.displayError)
4349
- this.handleErrorWithSnackBar(err);
4554
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage);
4350
4555
  this.isPending.set(false);
4351
4556
  return this.handleError(err);
4352
4557
  }));
@@ -4357,9 +4562,13 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4357
4562
  const updatedOptions = this.defineReqOptions(options, params);
4358
4563
  const func = this.deleteRecordRequest;
4359
4564
  const requests = this.createRequest(func, updatedOptions);
4360
- return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => this.data.set(data)), finalize(() => this.isPending.set(false)), catchError((err) => {
4565
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => {
4566
+ this.data.set(data);
4567
+ if (updatedOptions.displaySuccess)
4568
+ this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4569
+ }), finalize(() => this.isPending.set(false)), catchError((err) => {
4361
4570
  if (updatedOptions.displayError)
4362
- this.handleErrorWithSnackBar(err);
4571
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage);
4363
4572
  this.isPending.set(false);
4364
4573
  return this.handleError(err);
4365
4574
  }));
@@ -4375,6 +4584,24 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4375
4584
  return this.handleError(err);
4376
4585
  }));
4377
4586
  }
4587
+ uploadRequest(files, options, params) {
4588
+ this.isPending.set(true);
4589
+ this.data.set(null);
4590
+ const updatedOptions = this.defineReqOptions(options, params);
4591
+ updatedOptions.uploadFiles = files;
4592
+ const func = this.uploadFileRequest;
4593
+ const requests = this.createRequest(func, updatedOptions);
4594
+ return this.createObservable(updatedOptions, requests, func.name).pipe(tap(data => {
4595
+ this.data.set(data);
4596
+ if (updatedOptions.displaySuccess)
4597
+ this.handleSuccessWithSnackBar(updatedOptions.successMessage);
4598
+ }), finalize(() => this.isPending.set(false)), catchError((err) => {
4599
+ if (updatedOptions.displayError)
4600
+ this.handleErrorWithSnackBar(err, updatedOptions.errorMessage || err?.message);
4601
+ this.isPending.set(false);
4602
+ return this.handleError(err);
4603
+ }));
4604
+ }
4378
4605
  // --- private helpers ---
4379
4606
  createObservable(options, request$, funcName) {
4380
4607
  const polling = options.polling ? (options.polling > 0 ? true : false) : false;
@@ -4444,9 +4671,9 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4444
4671
  this.error.set(error.message || `${error.status} - ${error.statusText}`);
4445
4672
  return throwError(() => error);
4446
4673
  }
4447
- handleErrorWithSnackBar(error) {
4674
+ handleErrorWithSnackBar(error, message) {
4448
4675
  const displayError = ToastDisplay.adapt({
4449
- message: error.message || `${error.status} - ${error.statusText}`,
4676
+ message: message || error.message || `${error.status} - ${error.statusText}`,
4450
4677
  action: 'OK',
4451
4678
  color: ToastColors.ERROR,
4452
4679
  icon: 'error',
@@ -4456,7 +4683,7 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4456
4683
  }
4457
4684
  handleSuccessWithSnackBar(message) {
4458
4685
  const displaySuccess = ToastDisplay.adapt({
4459
- message: message || 'OK',
4686
+ message: message || 'Success',
4460
4687
  action: 'OK',
4461
4688
  color: ToastColors.SUCCESS,
4462
4689
  icon: 'check_circle',
@@ -4499,16 +4726,16 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4499
4726
  * @param options Optional batch configuration
4500
4727
  * @returns Observable emitting array of data when all requests complete
4501
4728
  */
4502
- getSequentialRequest(requests, options) {
4729
+ getSequentialRequest(requests, options, successMessage, errorMessage) {
4503
4730
  const batchOptions = BatchOptions.adapt(options);
4504
4731
  const results = new Array(requests.length);
4505
4732
  return from(requests).pipe(concatMap((req, index) => {
4506
- return this.getRequest(req).pipe(tap(data => {
4733
+ return this.getRequest(req, undefined).pipe(tap(data => {
4507
4734
  results[index] = data;
4508
4735
  }), catchError(error => this.handleSequentialError(req, error, index, batchOptions)));
4509
4736
  }), toArray(), tap(() => {
4510
4737
  if (batchOptions.displaySuccess)
4511
- this.handleSuccessWithSnackBar(batchOptions.successMessage);
4738
+ this.handleSuccessWithSnackBar(successMessage);
4512
4739
  }), map(() => results));
4513
4740
  }
4514
4741
  /**
@@ -4517,17 +4744,17 @@ class HTTPManagerSignalsService extends RequestSignalsService {
4517
4744
  * @param options Optional batch configuration with concurrency limit
4518
4745
  * @returns Observable emitting array of data when all requests complete
4519
4746
  */
4520
- getParallelRequest(requests, options) {
4747
+ getParallelRequest(requests, options, successMessage, errorMessage) {
4521
4748
  const batchOptions = BatchOptions.adapt(options);
4522
4749
  const concurrency = batchOptions.concurrency || 3;
4523
4750
  const results = new Array(requests.length);
4524
4751
  return from(requests).pipe(mergeMap((req, index) => {
4525
- return this.getRequest(req).pipe(tap(data => {
4752
+ return this.getRequest(req, undefined).pipe(tap(data => {
4526
4753
  results[index] = data;
4527
4754
  }), catchError(error => this.handleParallelError(req, error, index, batchOptions)));
4528
4755
  }, concurrency), toArray(), tap(() => {
4529
4756
  if (batchOptions.displaySuccess)
4530
- this.handleSuccessWithSnackBar(batchOptions.successMessage);
4757
+ this.handleSuccessWithSnackBar(successMessage);
4531
4758
  }), map(() => results));
4532
4759
  }
4533
4760
  /**
@@ -4632,7 +4859,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4632
4859
  }] }] });
4633
4860
 
4634
4861
  class ApiRequest {
4635
- constructor(server = '', path, headers, adapter, mapper, polling, retry, stream, streamType, displayError, displaySuccess, successMessage, saveAs, fileContentHeader, ws, env) {
4862
+ constructor(server = '', path, headers, adapter, mapper, polling, retry, stream, streamType, displayError, displaySuccess, successMessage, errorMessage, saveAs, fileContentHeader, ws, env, uploadFiles, uploadFieldName, uploadHttpMethod, allowedTypes, maxFileSize, maxTotalSize) {
4636
4863
  this.server = server;
4637
4864
  this.path = path;
4638
4865
  this.headers = headers;
@@ -4645,14 +4872,21 @@ class ApiRequest {
4645
4872
  this.displayError = displayError;
4646
4873
  this.displaySuccess = displaySuccess;
4647
4874
  this.successMessage = successMessage;
4875
+ this.errorMessage = errorMessage;
4648
4876
  this.saveAs = saveAs;
4649
4877
  this.fileContentHeader = fileContentHeader;
4650
4878
  this.ws = ws;
4651
4879
  this.env = env;
4880
+ this.uploadFiles = uploadFiles;
4881
+ this.uploadFieldName = uploadFieldName;
4882
+ this.uploadHttpMethod = uploadHttpMethod;
4883
+ this.allowedTypes = allowedTypes;
4884
+ this.maxFileSize = maxFileSize;
4885
+ this.maxTotalSize = maxTotalSize;
4652
4886
  }
4653
4887
  static adapt(item) {
4654
4888
  const server = Array.isArray(item?.server) ? item.server.join('/') : item?.server || '';
4655
- return new ApiRequest(server, (item?.path) ? item.path : [], (item?.headers) ? item.headers : {}, item?.adapter, item?.mapper, item?.polling ? Math.floor(item.polling) : 0, item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt(), (item?.stream) ? item.stream : false, item?.streamType || StreamType.AI_STREAMING, (item?.displayError) ? item.displayError : false, (item?.displaySuccess) ? item.displaySuccess : false, item?.successMessage, item?.saveAs, item?.fileContentHeader, item?.ws, item?.env || 'dev');
4889
+ return new ApiRequest(server, item?.path ? item.path : [], item?.headers ? item.headers : {}, item?.adapter, item?.mapper, item?.polling ? Math.floor(item.polling) : 0, item?.retry ? RetryOptions.adapt(item.retry) : RetryOptions.adapt(), item?.stream ? item.stream : false, item?.streamType || StreamType.AI_STREAMING, item?.displayError ? item.displayError : false, item?.displaySuccess ? item.displaySuccess : false, item?.successMessage, item?.errorMessage, item?.saveAs, item?.fileContentHeader, item?.ws, item?.env || 'dev', item?.uploadFiles, item?.uploadFieldName, item?.uploadHttpMethod, Array.isArray(item?.allowedTypes) ? item.allowedTypes : [], item?.maxFileSize, item?.maxTotalSize);
4656
4890
  }
4657
4891
  }
4658
4892
 
@@ -7231,9 +7465,11 @@ class RequestHeadersInterceptor {
7231
7465
  }));
7232
7466
  }
7233
7467
  intercept(request, next) {
7468
+ // Don't override Content-Type for FormData (file uploads) — let browser set multipart/form-data
7469
+ const skipContentType = request.body instanceof FormData;
7234
7470
  request = request.clone({
7235
7471
  setHeaders: {
7236
- 'Content-Type': 'application/json',
7472
+ ...(skipContentType ? {} : { 'Content-Type': 'application/json' }),
7237
7473
  'Accept-Language': this.language || 'en-CA',
7238
7474
  'Current-Date': this.currentDate
7239
7475
  }
@@ -10383,6 +10619,133 @@ class DatabaseDataSource extends DataSource {
10383
10619
  }
10384
10620
  }
10385
10621
 
10622
+ class UploadStateModel {
10623
+ constructor(selectedFiles = [], isUploading = false, progress = 0, currentFile = null, errorMessage = null, successMessage = null) {
10624
+ this.selectedFiles = selectedFiles;
10625
+ this.isUploading = isUploading;
10626
+ this.progress = progress;
10627
+ this.currentFile = currentFile;
10628
+ this.errorMessage = errorMessage;
10629
+ this.successMessage = successMessage;
10630
+ }
10631
+ static adapt(item) {
10632
+ return new UploadStateModel(Array.isArray(item?.selectedFiles) ? item.selectedFiles : [], item?.isUploading ?? false, item?.progress ?? 0, item?.currentFile ?? null, item?.errorMessage ?? null, item?.successMessage ?? null);
10633
+ }
10634
+ }
10635
+
10636
+ class UploadDemoComponent {
10637
+ constructor() {
10638
+ this.httpManager = inject(HTTPManagerService);
10639
+ this.toastService = inject(ToastMessageDisplayService);
10640
+ this.uploadState = UploadStateModel.adapt();
10641
+ this.allowedTypes = ['image/jpeg', 'image/png'];
10642
+ this.maxFileSize = 40 * 1024 * 1024; // 40MB
10643
+ }
10644
+ ngOnInit() {
10645
+ // Initialize component
10646
+ }
10647
+ ngOnDestroy() {
10648
+ // Clean up subscriptions
10649
+ }
10650
+ onFileSelected(event) {
10651
+ const input = event.target;
10652
+ const files = input.files;
10653
+ if (!files || files.length === 0) {
10654
+ return;
10655
+ }
10656
+ this.uploadState.selectedFiles = [];
10657
+ this.uploadState.errorMessage = null;
10658
+ for (let i = 0; i < files.length; i++) {
10659
+ const file = files[i];
10660
+ const validationError = this.validateFile(file);
10661
+ if (validationError) {
10662
+ this.uploadState.errorMessage = validationError;
10663
+ continue;
10664
+ }
10665
+ this.uploadState.selectedFiles.push(file);
10666
+ }
10667
+ if (this.uploadState.selectedFiles.length === 0 && !this.uploadState.errorMessage) {
10668
+ this.uploadState.errorMessage = 'Please select at least one valid file (JPG or PNG)';
10669
+ }
10670
+ }
10671
+ validateFile(file) {
10672
+ // Check file type
10673
+ if (!this.allowedTypes.includes(file.type)) {
10674
+ return `File "${file.name}" is not a valid type. Only JPG and PNG files are allowed.`;
10675
+ }
10676
+ // Check file size
10677
+ if (file.size > this.maxFileSize) {
10678
+ return `File "${file.name}" exceeds maximum size of 10MB.`;
10679
+ }
10680
+ return null;
10681
+ }
10682
+ clearSelection() {
10683
+ this.uploadState.selectedFiles = [];
10684
+ this.uploadState.errorMessage = null;
10685
+ this.uploadState.successMessage = null;
10686
+ this.uploadState.progress = 0;
10687
+ this.uploadState.currentFile = null;
10688
+ }
10689
+ startUpload() {
10690
+ if (this.uploadState.selectedFiles.length === 0) {
10691
+ this.uploadState.errorMessage = 'Please select at least one file to upload';
10692
+ return;
10693
+ }
10694
+ if (this.uploadState.errorMessage) {
10695
+ return;
10696
+ }
10697
+ this.uploadState.isUploading = true;
10698
+ this.uploadState.progress = 0;
10699
+ this.uploadState.errorMessage = null;
10700
+ this.uploadState.successMessage = null;
10701
+ const options = ApiRequest.adapt({
10702
+ server: 'gtlcApi',
10703
+ path: ['uploader'],
10704
+ uploadFieldName: 'files',
10705
+ allowedTypes: this.allowedTypes,
10706
+ maxFileSize: this.maxFileSize
10707
+ });
10708
+ this.httpManager.uploadRequest(this.uploadState.selectedFiles, options, ['upload'])
10709
+ .subscribe({
10710
+ next: (response) => {
10711
+ this.uploadState.progress = 100;
10712
+ this.uploadState.successMessage = `Successfully uploaded ${this.uploadState.selectedFiles.length} file(s)`;
10713
+ this.toastService.toastMessage({
10714
+ message: this.uploadState.successMessage || '',
10715
+ color: ToastColors.SUCCESS
10716
+ });
10717
+ this.uploadState.isUploading = false;
10718
+ },
10719
+ error: (error) => {
10720
+ this.uploadState.errorMessage = error.message || 'Upload failed. Please try again.';
10721
+ this.toastService.toastMessage({
10722
+ message: this.uploadState.errorMessage || '',
10723
+ color: ToastColors.ERROR
10724
+ });
10725
+ this.uploadState.isUploading = false;
10726
+ this.uploadState.progress = 0;
10727
+ }
10728
+ });
10729
+ }
10730
+ getFileSizeString(size) {
10731
+ if (size < 1024) {
10732
+ return `${size} bytes`;
10733
+ }
10734
+ else if (size < 1024 * 1024) {
10735
+ return `${(size / 1024).toFixed(1)} KB`;
10736
+ }
10737
+ else {
10738
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
10739
+ }
10740
+ }
10741
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UploadDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10742
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: UploadDemoComponent, selector: "lib-upload-demo", ngImport: i0, template: "<div class=\"upload-demo-container\">\n <h2>File Upload Demo</h2>\n <p class=\"demo-description\">Upload multiple JPG or PNG files with progress tracking</p>\n\n <!-- Upload Button & File Input -->\n <div class=\"upload-controls\">\n <button\n mat-raised-button\n color=\"primary\"\n (click)=\"fileInput.click()\"\n [disabled]=\"uploadState.isUploading\"\n class=\"upload-button\">\n <span class=\"material-icons\">upload_file</span>\n Select Files\n </button>\n <input\n #fileInput\n type=\"file\"\n multiple\n accept=\"image/jpeg,image/png\"\n (change)=\"onFileSelected($event)\"\n [disabled]=\"uploadState.isUploading\"\n hidden>\n\n <button\n mat-button\n color=\"warn\"\n (click)=\"clearSelection()\"\n [disabled]=\"uploadState.selectedFiles.length === 0 || uploadState.isUploading\"\n class=\"clear-button\">\n Clear\n </button>\n </div>\n\n <!-- Selected Files Display -->\n <div class=\"selected-files\" *ngIf=\"uploadState.selectedFiles.length > 0\">\n <h3>Selected Files ({{uploadState.selectedFiles.length}})</h3>\n <ul class=\"file-list\">\n <li *ngFor=\"let file of uploadState.selectedFiles\" class=\"file-item\">\n <span class=\"file-icon\">\n <span class=\"material-icons\">insert_drive_file</span>\n </span>\n <span class=\"file-name\">{{file.name}}</span>\n <span class=\"file-size\">{{getFileSizeString(file.size)}}</span>\n <span class=\"file-status valid\">\u2713</span>\n </li>\n </ul>\n </div>\n\n <!-- Validation Errors -->\n <div class=\"error-message\" *ngIf=\"uploadState.errorMessage && !uploadState.isUploading\">\n <span class=\"material-icons\">error</span>\n <span>{{uploadState.errorMessage}}</span>\n </div>\n\n <!-- Progress Bar -->\n <div class=\"progress-container\" *ngIf=\"uploadState.isUploading\">\n <div class=\"progress-header\">\n <span>Uploading...</span>\n <span class=\"progress-percentage\">{{uploadState.progress}}%</span>\n </div>\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"uploadState.progress\"\n color=\"primary\">\n </mat-progress-bar>\n <div class=\"current-file\" *ngIf=\"uploadState.currentFile\">\n Current: {{uploadState.currentFile}}\n </div>\n </div>\n\n <!-- Success Message -->\n <div class=\"success-message\" *ngIf=\"uploadState.successMessage\">\n <span class=\"material-icons\">check_circle</span>\n <span>{{uploadState.successMessage}}</span>\n </div>\n\n <!-- Upload Action Button -->\n <div class=\"upload-actions\" *ngIf=\"uploadState.selectedFiles.length > 0 && !uploadState.isUploading\">\n <button\n mat-raised-button\n color=\"primary\"\n (click)=\"startUpload()\"\n [disabled]=\"uploadState.selectedFiles.length === 0 || uploadState.errorMessage !== null\"\n class=\"start-upload-button\">\n Upload {{uploadState.selectedFiles.length}} File{{uploadState.selectedFiles.length > 1 ? 's' : ''}}\n </button>\n </div>\n</div>\n", styles: [".upload-demo-container{padding:1rem;max-width:60rem;margin:0 auto}.upload-demo-container h2{font-size:2rem;font-weight:700;color:#333;margin-bottom:.75rem}.upload-demo-container .demo-description{font-size:1rem;color:#666;margin-bottom:1.5rem}.upload-demo-container .upload-controls{display:flex;gap:1rem;margin-bottom:1.5rem;align-items:center}.upload-demo-container .upload-controls .upload-button,.upload-demo-container .upload-controls .clear-button{min-width:10rem}.upload-demo-container .selected-files{background-color:#fbfbfb;border:1px solid #ccc;border-radius:.25rem;padding:1rem;margin-bottom:1.5rem}.upload-demo-container .selected-files h3{font-size:1.125rem;font-weight:500;color:#333;margin-bottom:.5rem}.upload-demo-container .selected-files .file-list{list-style:none;padding:0;margin:0}.upload-demo-container .selected-files .file-list .file-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;border-bottom:1px solid #e5e5e5}.upload-demo-container .selected-files .file-list .file-item:last-child{border-bottom:none}.upload-demo-container .selected-files .file-list .file-item .file-icon{color:#286fad;display:flex;align-items:center}.upload-demo-container .selected-files .file-list .file-item .file-name{flex:1;font-size:.875rem;color:#333;font-weight:500}.upload-demo-container .selected-files .file-list .file-item .file-size{font-size:.875rem;color:#666}.upload-demo-container .selected-files .file-list .file-item .file-status.valid{color:#1c8150;font-weight:700}.upload-demo-container .error-message{display:flex;align-items:center;gap:.5rem;padding:1rem;background-color:#d400031a;border:1px solid #d40003;border-radius:.25rem;color:#d40003;margin-bottom:1.5rem;font-size:.875rem}.upload-demo-container .error-message .material-icons{font-size:2.5rem}.upload-demo-container .progress-container{margin-bottom:1.5rem}.upload-demo-container .progress-container .progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.25rem}.upload-demo-container .progress-container .progress-header span:first-child{font-size:.875rem;font-weight:500;color:#333}.upload-demo-container .progress-container .progress-header .progress-percentage{font-size:.875rem;font-weight:700;color:#286fad}.upload-demo-container .progress-container .current-file{font-size:.875rem;color:#666;margin-top:.25rem;font-style:italic}.upload-demo-container .success-message{display:flex;align-items:center;gap:.5rem;padding:1rem;background-color:#1c81501a;border:1px solid #1c8150;border-radius:.25rem;color:#1c8150;margin-bottom:1.5rem;font-size:.875rem}.upload-demo-container .success-message .material-icons{font-size:2.5rem}.upload-demo-container .upload-actions{text-align:center;margin-top:1.5rem}.upload-demo-container .upload-actions .start-upload-button{min-width:15rem;padding:.5rem 1.5rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i8$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }] }); }
10743
+ }
10744
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: UploadDemoComponent, decorators: [{
10745
+ type: Component,
10746
+ args: [{ selector: 'lib-upload-demo', standalone: false, template: "<div class=\"upload-demo-container\">\n <h2>File Upload Demo</h2>\n <p class=\"demo-description\">Upload multiple JPG or PNG files with progress tracking</p>\n\n <!-- Upload Button & File Input -->\n <div class=\"upload-controls\">\n <button\n mat-raised-button\n color=\"primary\"\n (click)=\"fileInput.click()\"\n [disabled]=\"uploadState.isUploading\"\n class=\"upload-button\">\n <span class=\"material-icons\">upload_file</span>\n Select Files\n </button>\n <input\n #fileInput\n type=\"file\"\n multiple\n accept=\"image/jpeg,image/png\"\n (change)=\"onFileSelected($event)\"\n [disabled]=\"uploadState.isUploading\"\n hidden>\n\n <button\n mat-button\n color=\"warn\"\n (click)=\"clearSelection()\"\n [disabled]=\"uploadState.selectedFiles.length === 0 || uploadState.isUploading\"\n class=\"clear-button\">\n Clear\n </button>\n </div>\n\n <!-- Selected Files Display -->\n <div class=\"selected-files\" *ngIf=\"uploadState.selectedFiles.length > 0\">\n <h3>Selected Files ({{uploadState.selectedFiles.length}})</h3>\n <ul class=\"file-list\">\n <li *ngFor=\"let file of uploadState.selectedFiles\" class=\"file-item\">\n <span class=\"file-icon\">\n <span class=\"material-icons\">insert_drive_file</span>\n </span>\n <span class=\"file-name\">{{file.name}}</span>\n <span class=\"file-size\">{{getFileSizeString(file.size)}}</span>\n <span class=\"file-status valid\">\u2713</span>\n </li>\n </ul>\n </div>\n\n <!-- Validation Errors -->\n <div class=\"error-message\" *ngIf=\"uploadState.errorMessage && !uploadState.isUploading\">\n <span class=\"material-icons\">error</span>\n <span>{{uploadState.errorMessage}}</span>\n </div>\n\n <!-- Progress Bar -->\n <div class=\"progress-container\" *ngIf=\"uploadState.isUploading\">\n <div class=\"progress-header\">\n <span>Uploading...</span>\n <span class=\"progress-percentage\">{{uploadState.progress}}%</span>\n </div>\n <mat-progress-bar\n mode=\"determinate\"\n [value]=\"uploadState.progress\"\n color=\"primary\">\n </mat-progress-bar>\n <div class=\"current-file\" *ngIf=\"uploadState.currentFile\">\n Current: {{uploadState.currentFile}}\n </div>\n </div>\n\n <!-- Success Message -->\n <div class=\"success-message\" *ngIf=\"uploadState.successMessage\">\n <span class=\"material-icons\">check_circle</span>\n <span>{{uploadState.successMessage}}</span>\n </div>\n\n <!-- Upload Action Button -->\n <div class=\"upload-actions\" *ngIf=\"uploadState.selectedFiles.length > 0 && !uploadState.isUploading\">\n <button\n mat-raised-button\n color=\"primary\"\n (click)=\"startUpload()\"\n [disabled]=\"uploadState.selectedFiles.length === 0 || uploadState.errorMessage !== null\"\n class=\"start-upload-button\">\n Upload {{uploadState.selectedFiles.length}} File{{uploadState.selectedFiles.length > 1 ? 's' : ''}}\n </button>\n </div>\n</div>\n", styles: [".upload-demo-container{padding:1rem;max-width:60rem;margin:0 auto}.upload-demo-container h2{font-size:2rem;font-weight:700;color:#333;margin-bottom:.75rem}.upload-demo-container .demo-description{font-size:1rem;color:#666;margin-bottom:1.5rem}.upload-demo-container .upload-controls{display:flex;gap:1rem;margin-bottom:1.5rem;align-items:center}.upload-demo-container .upload-controls .upload-button,.upload-demo-container .upload-controls .clear-button{min-width:10rem}.upload-demo-container .selected-files{background-color:#fbfbfb;border:1px solid #ccc;border-radius:.25rem;padding:1rem;margin-bottom:1.5rem}.upload-demo-container .selected-files h3{font-size:1.125rem;font-weight:500;color:#333;margin-bottom:.5rem}.upload-demo-container .selected-files .file-list{list-style:none;padding:0;margin:0}.upload-demo-container .selected-files .file-list .file-item{display:flex;align-items:center;gap:.5rem;padding:.25rem 0;border-bottom:1px solid #e5e5e5}.upload-demo-container .selected-files .file-list .file-item:last-child{border-bottom:none}.upload-demo-container .selected-files .file-list .file-item .file-icon{color:#286fad;display:flex;align-items:center}.upload-demo-container .selected-files .file-list .file-item .file-name{flex:1;font-size:.875rem;color:#333;font-weight:500}.upload-demo-container .selected-files .file-list .file-item .file-size{font-size:.875rem;color:#666}.upload-demo-container .selected-files .file-list .file-item .file-status.valid{color:#1c8150;font-weight:700}.upload-demo-container .error-message{display:flex;align-items:center;gap:.5rem;padding:1rem;background-color:#d400031a;border:1px solid #d40003;border-radius:.25rem;color:#d40003;margin-bottom:1.5rem;font-size:.875rem}.upload-demo-container .error-message .material-icons{font-size:2.5rem}.upload-demo-container .progress-container{margin-bottom:1.5rem}.upload-demo-container .progress-container .progress-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.25rem}.upload-demo-container .progress-container .progress-header span:first-child{font-size:.875rem;font-weight:500;color:#333}.upload-demo-container .progress-container .progress-header .progress-percentage{font-size:.875rem;font-weight:700;color:#286fad}.upload-demo-container .progress-container .current-file{font-size:.875rem;color:#666;margin-top:.25rem;font-style:italic}.upload-demo-container .success-message{display:flex;align-items:center;gap:.5rem;padding:1rem;background-color:#1c81501a;border:1px solid #1c8150;border-radius:.25rem;color:#1c8150;margin-bottom:1.5rem;font-size:.875rem}.upload-demo-container .success-message .material-icons{font-size:2.5rem}.upload-demo-container .upload-actions{text-align:center;margin-top:1.5rem}.upload-demo-container .upload-actions .start-upload-button{min-width:15rem;padding:.5rem 1.5rem}\n"] }]
10747
+ }] });
10748
+
10386
10749
  class HttpRequestServicesDemoComponent {
10387
10750
  constructor(configOptions) {
10388
10751
  this.configOptions = configOptions;
@@ -10395,6 +10758,7 @@ class HttpRequestServicesDemoComponent {
10395
10758
  // { name: "Http Signals Service", value: 'http_signals_service', new: true },
10396
10759
  { name: "Http State Service", value: 'http_state_service' },
10397
10760
  { name: "Http State Service - Websockets", value: 'http_state_service_ws', new: false },
10761
+ { name: "File Upload Demo", value: 'file_upload_demo', new: true, divider: true },
10398
10762
  { name: "Database Service", value: 'database_service', divider: true, disabled: false },
10399
10763
  { name: "Local Storage Service", value: 'local_storage_service' },
10400
10764
  // { name: "Local Signals Storage Service", value: 'local_storage_signals_service', new: true },
@@ -10411,11 +10775,11 @@ class HttpRequestServicesDemoComponent {
10411
10775
  this.selectedService = this.requestTypes[type].value;
10412
10776
  }
10413
10777
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestServicesDemoComponent, deps: [{ token: CONFIG_SETTINGS_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
10414
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: HttpRequestServicesDemoComponent, selector: "app-http-request-services-demo", inputs: { wsServer: "wsServer", jwtToken: "jwtToken", server: "server", user: "user", path: "path", adapter: "adapter", mapper: "mapper" }, ngImport: i0, template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n @for (type of requestTypes; track type; let i = $index) {\n @if (type?.divider) {\n <div\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n }\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n }\n\n </mat-menu>\n</mat-toolbar>\n\n<span>\n @switch (selectedService) {\n @case ('basic_http_service') {\n <p>\n <app-request-manager-basic-demo></app-request-manager-basic-demo>\n </p>\n }\n @case ('http_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'http_signals_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-signals-manager-demo></app-request-signals-manager-demo>\n </p> -->\n @case ('http_state_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-state-demo>\n </p>\n }\n @case ('http_state_service_ws') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-ws-demo\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-request-manager-ws-demo>\n </p>\n }\n @case ('database_service') {\n <p>\n <app-database-data-demo></app-database-data-demo>\n </p>\n }\n @case ('local_storage_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'local_storage_signals_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-signals-demo></app-local-storage-signals-demo>\n</p> -->\n@case ('store_state_manager') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-store-state-manager-demo></app-store-state-manager-demo>\n </p>\n}\n@default {\n <p>\n Other\n </p>\n}\n}\n</span>\n\n<ng-template #HTTP_OPTIONS>\n @if (injectionOptions?.httpRequestOptions) {\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n }\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n @if (injectionOptions?.LocalStorageOptions) {\n <ng-container class=\"box\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n }\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: RequestManagerBasicDemoComponent, selector: "app-request-manager-basic-demo" }, { kind: "component", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", inputs: ["server", "adapter", "mapper"] }, { kind: "component", type: RequestManagerDemoComponent, selector: "app-request-manager-demo", inputs: ["server", "adapter", "mapper"] }, { kind: "component", type: LocalStorageDemoComponent, selector: "app-local-storage-demo" }, { kind: "component", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: StoreStateManagerDemoComponent, selector: "app-store-state-manager-demo" }, { kind: "component", type: DatabaseDataDemoComponent, selector: "app-database-data-demo" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
10778
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: HttpRequestServicesDemoComponent, selector: "app-http-request-services-demo", inputs: { wsServer: "wsServer", jwtToken: "jwtToken", server: "server", user: "user", path: "path", adapter: "adapter", mapper: "mapper" }, ngImport: i0, template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n @for (type of requestTypes; track type; let i = $index) {\n @if (type?.divider) {\n <div\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n }\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n }\n\n </mat-menu>\n</mat-toolbar>\n\n<span>\n @switch (selectedService) {\n @case ('basic_http_service') {\n <p>\n <app-request-manager-basic-demo></app-request-manager-basic-demo>\n </p>\n }\n @case ('http_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'http_signals_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-signals-manager-demo></app-request-signals-manager-demo>\n </p> -->\n @case ('http_state_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-state-demo>\n </p>\n }\n @case ('http_state_service_ws') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-ws-demo\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-request-manager-ws-demo>\n </p>\n }\n @case ('file_upload_demo') {\n <p>\n <lib-upload-demo></lib-upload-demo>\n </p>\n }\n @case ('database_service') {\n <p>\n <app-database-data-demo></app-database-data-demo>\n </p>\n }\n @case ('local_storage_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'local_storage_signals_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-signals-demo></app-local-storage-signals-demo>\n</p> -->\n@case ('store_state_manager') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-store-state-manager-demo></app-store-state-manager-demo>\n </p>\n}\n@default {\n <p>\n Other\n </p>\n}\n}\n</span>\n\n<ng-template #HTTP_OPTIONS>\n @if (injectionOptions?.httpRequestOptions) {\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n }\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n @if (injectionOptions?.LocalStorageOptions) {\n <ng-container class=\"box\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n }\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i3.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i7.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i7.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i7.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: RequestManagerBasicDemoComponent, selector: "app-request-manager-basic-demo" }, { kind: "component", type: RequestManagerStateDemoComponent, selector: "app-request-manager-state-demo", inputs: ["server", "adapter", "mapper"] }, { kind: "component", type: RequestManagerDemoComponent, selector: "app-request-manager-demo", inputs: ["server", "adapter", "mapper"] }, { kind: "component", type: LocalStorageDemoComponent, selector: "app-local-storage-demo" }, { kind: "component", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: ["server", "wsServer", "jwtToken", "user", "path"] }, { kind: "component", type: StoreStateManagerDemoComponent, selector: "app-store-state-manager-demo" }, { kind: "component", type: DatabaseDataDemoComponent, selector: "app-database-data-demo" }, { kind: "component", type: UploadDemoComponent, selector: "lib-upload-demo" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
10415
10779
  }
10416
10780
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestServicesDemoComponent, decorators: [{
10417
10781
  type: Component,
10418
- args: [{ selector: 'app-http-request-services-demo', standalone: false, template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n @for (type of requestTypes; track type; let i = $index) {\n @if (type?.divider) {\n <div\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n }\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n }\n\n </mat-menu>\n</mat-toolbar>\n\n<span>\n @switch (selectedService) {\n @case ('basic_http_service') {\n <p>\n <app-request-manager-basic-demo></app-request-manager-basic-demo>\n </p>\n }\n @case ('http_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'http_signals_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-signals-manager-demo></app-request-signals-manager-demo>\n </p> -->\n @case ('http_state_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-state-demo>\n </p>\n }\n @case ('http_state_service_ws') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-ws-demo\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-request-manager-ws-demo>\n </p>\n }\n @case ('database_service') {\n <p>\n <app-database-data-demo></app-database-data-demo>\n </p>\n }\n @case ('local_storage_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'local_storage_signals_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-signals-demo></app-local-storage-signals-demo>\n</p> -->\n@case ('store_state_manager') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-store-state-manager-demo></app-store-state-manager-demo>\n </p>\n}\n@default {\n <p>\n Other\n </p>\n}\n}\n</span>\n\n<ng-template #HTTP_OPTIONS>\n @if (injectionOptions?.httpRequestOptions) {\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n }\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n @if (injectionOptions?.LocalStorageOptions) {\n <ng-container class=\"box\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n }\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"] }]
10782
+ args: [{ selector: 'app-http-request-services-demo', standalone: false, template: "<mat-toolbar style=\"display:flex\">\n <div>Http Request Manager Services</div>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button [matMenuTriggerFor]=\"menu\">Services</button>\n <mat-menu #menu=\"matMenu\">\n @for (type of requestTypes; track type; let i = $index) {\n @if (type?.divider) {\n <div\n style=\"margin-top: .5rem; margin-bottom: .5rem;\"\n >\n <mat-divider></mat-divider>\n </div>\n }\n <button\n mat-menu-item\n (click)=\"onSelected(i)\"\n [disabled]=\"type.disabled\"\n >\n {{ type.name }}\n </button>\n }\n\n </mat-menu>\n</mat-toolbar>\n\n<span>\n @switch (selectedService) {\n @case ('basic_http_service') {\n <p>\n <app-request-manager-basic-demo></app-request-manager-basic-demo>\n </p>\n }\n @case ('http_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'http_signals_service'\">\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-signals-manager-demo></app-request-signals-manager-demo>\n </p> -->\n @case ('http_state_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-state-demo\n [server]=\"server\"\n [adapter]=\"adapter\"\n [mapper]=\"mapper\"\n ></app-request-manager-state-demo>\n </p>\n }\n @case ('http_state_service_ws') {\n <p>\n <ng-container *ngTemplateOutlet=\"HTTP_OPTIONS\"></ng-container>\n <app-request-manager-ws-demo\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n ></app-request-manager-ws-demo>\n </p>\n }\n @case ('file_upload_demo') {\n <p>\n <lib-upload-demo></lib-upload-demo>\n </p>\n }\n @case ('database_service') {\n <p>\n <app-database-data-demo></app-database-data-demo>\n </p>\n }\n @case ('local_storage_service') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-demo></app-local-storage-demo>\n </p>\n }\n <!-- <p *ngSwitchCase=\"'local_storage_signals_service'\">\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-local-storage-signals-demo></app-local-storage-signals-demo>\n</p> -->\n@case ('store_state_manager') {\n <p>\n <ng-container *ngTemplateOutlet=\"LOCAL_OPTIONS\"></ng-container>\n <app-store-state-manager-demo></app-store-state-manager-demo>\n </p>\n}\n@default {\n <p>\n Other\n </p>\n}\n}\n</span>\n\n<ng-template #HTTP_OPTIONS>\n @if (injectionOptions?.httpRequestOptions) {\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - HTTP Options</h3>\n {{ injectionOptions?.httpRequestOptions| json }}\n </div>\n }\n</ng-template>\n\n<ng-template #LOCAL_OPTIONS>\n @if (injectionOptions?.LocalStorageOptions) {\n <ng-container class=\"box\">\n <div class=\"box\">\n <h3 style=\"font-weight: bold;\">Injection Token Detected - LocalStorage Options</h3>\n {{ injectionOptions?.LocalStorageOptions| json }}\n </div>\n </ng-container>\n }\n</ng-template>\n\n\n", styles: [".box{padding:1rem;background-color:#f5f5f5;border:thin gray solid;margin-top:1rem}\n"] }]
10419
10783
  }], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
10420
10784
  type: Inject,
10421
10785
  args: [CONFIG_SETTINGS_TOKEN]
@@ -10975,7 +11339,8 @@ class HttpRequestManagerModule {
10975
11339
  WsMessagingComponent,
10976
11340
  WsNotificationsComponent,
10977
11341
  WsAiMessagingComponent,
10978
- WsChatsComponent], imports: [CommonModule,
11342
+ WsChatsComponent,
11343
+ UploadDemoComponent], imports: [CommonModule,
10979
11344
  ToastMessageDisplayModule,
10980
11345
  FormsModule,
10981
11346
  ReactiveFormsModule,
@@ -10999,7 +11364,8 @@ class HttpRequestManagerModule {
10999
11364
  MatDatepickerModule,
11000
11365
  MatNativeDateModule,
11001
11366
  MatCardModule,
11002
- FileDownloaderModule], exports: [HttpRequestServicesDemoComponent] }); }
11367
+ FileDownloaderModule], exports: [HttpRequestServicesDemoComponent,
11368
+ UploadDemoComponent] }); }
11003
11369
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestManagerModule, providers: [
11004
11370
  { provide: HTTP_INTERCEPTORS, useClass: WithCredentialsInterceptor, multi: true },
11005
11371
  { provide: HTTP_INTERCEPTORS, useClass: RequestHeadersInterceptor, multi: true },
@@ -11087,9 +11453,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
11087
11453
  WsNotificationsComponent,
11088
11454
  WsAiMessagingComponent,
11089
11455
  WsChatsComponent,
11456
+ UploadDemoComponent,
11090
11457
  ],
11091
11458
  exports: [
11092
11459
  HttpRequestServicesDemoComponent,
11460
+ UploadDemoComponent,
11093
11461
  ],
11094
11462
  providers: [
11095
11463
  { provide: HTTP_INTERCEPTORS, useClass: WithCredentialsInterceptor, multi: true },
@@ -11240,5 +11608,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
11240
11608
  * Generated bundle index. Do not edit.
11241
11609
  */
11242
11610
 
11243
- export { ApiRequest, AppService, AsymmetricalEncryptionService, BatchOptions, BatchResult, CONFIG_SETTINGS_TOKEN, ChannelType, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseManagerService, DatabaseStorage, DbService, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsDemoComponent, LocalStorageSignalsManagerService, LoggerService, NotificationMessage, PathQueryService, PublicMessage, Random, RandomHSLColor, RandomHexColor, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomPaletteColor, RandomSignature, RandomStr, RandomVisibleColor, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RequestService, RequestSignalsService, RetryOptions, SettingOptions, StateMessage, StateStorageOptions, StorageData, StorageOption, StorageType, StoreStateManagerService, StoreStateManagerSignalsService, StoreStateSignalsDemoComponent, StreamType, SymmetricalEncryptionService, TableSchemaDef, UUID, UUID_STR, UserData, UtilsService, WSOptions, WebSocketMessageService, WithCredentialsInterceptor, calculateBatchProgress, countdown, createChannelName, delayedRetry, isErrorState, isPendingState, isSuccessState, requestPolling, requestStreaming, streamAI, streamAuto, streamEvents, streamJSON, streamNDJSON };
11611
+ export { ApiRequest, AppService, AsymmetricalEncryptionService, BatchOptions, BatchResult, CONFIG_SETTINGS_TOKEN, ChannelType, ConfigHTTPOptions, ConfigOptions, DataType, DatabaseDataDemoComponent, DatabaseManagerService, DatabaseStorage, DbService, ErrorDisplaySettings, GlobalStoreOptions, HTTPManagerService, HTTPManagerSignalsService, HTTPManagerStateService, HeadersService, HttpRequestManagerModule, HttpRequestServicesDemoComponent, InvalidFileInfoModel, LocalStorageDemoComponent, LocalStorageManagerService, LocalStorageOptions, LocalStorageSignalsDemoComponent, LocalStorageSignalsManagerService, LoggerService, NotificationMessage, PathQueryService, PublicMessage, Random, RandomHSLColor, RandomHexColor, RandomNumber, RandomNumbers, RandomNumbersUnique, RandomPaletteColor, RandomSignature, RandomStr, RandomVisibleColor, RequestErrorInterceptor, RequestHeadersInterceptor, RequestManagerDemoComponent, RequestManagerStateDemoComponent, RequestOptions, RequestService, RequestSignalsService, RetryOptions, SettingOptions, StateMessage, StateStorageOptions, StorageData, StorageOption, StorageType, StoreStateManagerService, StoreStateManagerSignalsService, StoreStateSignalsDemoComponent, StreamType, SymmetricalEncryptionService, TableSchemaDef, UUID, UUID_STR, UploadDemoComponent, UploadValidationErrorModel, UserData, UtilsService, WSOptions, WebSocketMessageService, WithCredentialsInterceptor, calculateBatchProgress, countdown, createChannelName, delayedRetry, isErrorState, isPendingState, isSuccessState, requestPolling, requestStreaming, streamAI, streamAuto, streamEvents, streamJSON, streamNDJSON };
11244
11612
  //# sourceMappingURL=http-request-manager.mjs.map