http-request-manager 18.9.5 → 18.10.0
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.
|
@@ -1163,7 +1163,13 @@ class ChannelInfo {
|
|
|
1163
1163
|
this.canSubscribe = canSubscribe;
|
|
1164
1164
|
}
|
|
1165
1165
|
static adapt(item) {
|
|
1166
|
-
|
|
1166
|
+
// Handle string input (backward compatibility with old server responses)
|
|
1167
|
+
if (typeof item === 'string') {
|
|
1168
|
+
return new ChannelInfo(item, true); // Assume subscribable
|
|
1169
|
+
}
|
|
1170
|
+
// Handle object input (new server responses)
|
|
1171
|
+
// Default canSubscribe to true if not specified (backward compat)
|
|
1172
|
+
return new ChannelInfo(item?.name, item?.canSubscribe ?? true);
|
|
1167
1173
|
}
|
|
1168
1174
|
}
|
|
1169
1175
|
|
|
@@ -2142,6 +2148,24 @@ class WebSocketManagerService {
|
|
|
2142
2148
|
console.error('WebSocket is not open. Cannot unsubscribe from notification channel.');
|
|
2143
2149
|
}
|
|
2144
2150
|
}
|
|
2151
|
+
/**
|
|
2152
|
+
* Send notification to channel
|
|
2153
|
+
* @param channel - Channel name (should include MES- prefix)
|
|
2154
|
+
* @param content - Notification content
|
|
2155
|
+
*/
|
|
2156
|
+
sendNotification(channel, content) {
|
|
2157
|
+
if (WebSocketManagerService.socket?.readyState === WebSocket.OPEN) {
|
|
2158
|
+
WebSocketManagerService.socket.send(JSON.stringify({
|
|
2159
|
+
type: 'notification',
|
|
2160
|
+
subscribedChannel: channel,
|
|
2161
|
+
content
|
|
2162
|
+
}));
|
|
2163
|
+
console.log(`📢 Sent notification to channel: ${channel}`, content);
|
|
2164
|
+
}
|
|
2165
|
+
else {
|
|
2166
|
+
console.error('WebSocket is not open. Cannot send notification.');
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2145
2169
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2146
2170
|
// CONNECTION STATUS (Static getter for direct access)
|
|
2147
2171
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -3072,6 +3096,12 @@ class HTTPManagerService extends RequestService {
|
|
|
3072
3096
|
unsubscribeFromNotificationChannel(channel) {
|
|
3073
3097
|
this.wsManager.unsubscribeFromNotificationChannel(channel);
|
|
3074
3098
|
}
|
|
3099
|
+
/**
|
|
3100
|
+
* Send notification to channel
|
|
3101
|
+
*/
|
|
3102
|
+
sendNotification(channel, content) {
|
|
3103
|
+
this.wsManager.sendNotification(channel, content);
|
|
3104
|
+
}
|
|
3075
3105
|
// REQUESTS
|
|
3076
3106
|
getRequest(options, params) {
|
|
3077
3107
|
this.isPending.next(true);
|
|
@@ -3173,7 +3203,9 @@ class HTTPManagerService extends RequestService {
|
|
|
3173
3203
|
}
|
|
3174
3204
|
return throwError(() => err);
|
|
3175
3205
|
}));
|
|
3176
|
-
return polling$.pipe(
|
|
3206
|
+
return polling$.pipe((options?.retry && options.retry.times > 0)
|
|
3207
|
+
? delayedRetry((options.retry.delay || 3) * 1000, (options.retry.times || 0) - 1)
|
|
3208
|
+
: (source) => source, catchError((err, caught) => {
|
|
3177
3209
|
if (err instanceof HttpErrorResponse) {
|
|
3178
3210
|
this.error.next(true);
|
|
3179
3211
|
if (isPolling)
|
|
@@ -3181,9 +3213,7 @@ class HTTPManagerService extends RequestService {
|
|
|
3181
3213
|
return this.handleError(err);
|
|
3182
3214
|
}
|
|
3183
3215
|
return throwError(() => err);
|
|
3184
|
-
})
|
|
3185
|
-
? delayedRetry((options.retry.delay || 3) * 1000, (options.retry.times || 0) - 1)
|
|
3186
|
-
: (source) => source);
|
|
3216
|
+
}));
|
|
3187
3217
|
}
|
|
3188
3218
|
createRequest(func, options, data) {
|
|
3189
3219
|
const dataItem = this.prepareRequestData(options, data, func.name);
|
|
@@ -3524,7 +3554,9 @@ class HTTPManagerSignalsService extends RequestSignalsService {
|
|
|
3524
3554
|
}
|
|
3525
3555
|
return throwError(() => err);
|
|
3526
3556
|
}));
|
|
3527
|
-
return polling$.pipe(
|
|
3557
|
+
return polling$.pipe(options?.retry && options.retry.times > 0
|
|
3558
|
+
? delayedRetry((options.retry.delay || 3) * 1000, (options.retry.times || 0) - 1)
|
|
3559
|
+
: (source) => source, catchError((err, caught) => {
|
|
3528
3560
|
if (err instanceof HttpErrorResponse) {
|
|
3529
3561
|
this.error.set(true);
|
|
3530
3562
|
if (isPolling)
|
|
@@ -3532,9 +3564,7 @@ class HTTPManagerSignalsService extends RequestSignalsService {
|
|
|
3532
3564
|
return this.handleError(err);
|
|
3533
3565
|
}
|
|
3534
3566
|
return throwError(() => err);
|
|
3535
|
-
})
|
|
3536
|
-
? delayedRetry((options.retry.delay || 3) * 1000, (options.retry.times || 0) - 1)
|
|
3537
|
-
: (source) => source);
|
|
3567
|
+
}));
|
|
3538
3568
|
}
|
|
3539
3569
|
createRequest(func, options, data) {
|
|
3540
3570
|
const dataItem = this.prepareRequestData(options, data, func.name);
|
|
@@ -4856,22 +4886,41 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
4856
4886
|
this.latestCommunicationMessages$ = this.latestCommunicationMessages.asObservable();
|
|
4857
4887
|
this.userAction = new BehaviorSubject(null);
|
|
4858
4888
|
this.userAction$ = this.userAction.asObservable();
|
|
4889
|
+
// Store our own sessionId for filtering out own messages
|
|
4890
|
+
this.ownSessionId = null;
|
|
4859
4891
|
this.wsOptions = WSOptions.adapt();
|
|
4860
4892
|
// Expose raw WS connection status directly to UI (from singleton WebSocketManagerService)
|
|
4861
4893
|
this.connectionStatus$ = this.httpManagerService.connectionStatus$;
|
|
4862
4894
|
// WebSocket
|
|
4863
4895
|
this.initWS = this.effect((wsOptions$) => wsOptions$.pipe(switchMap((wsOptions) => merge(this.httpManagerService.connectionStatus$.pipe(tap((isConnected) => {
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4896
|
+
if (isConnected) {
|
|
4897
|
+
// Subscribe to our private SYS- state channel so we receive stateMangerMessage broadcasts
|
|
4898
|
+
// This is the critical subscription that enables multi-client state syncing
|
|
4899
|
+
const stateChannel = wsOptions?.id || this.apiOptions.ws?.id;
|
|
4900
|
+
if (stateChannel) {
|
|
4901
|
+
console.log(`🔒 [STATE STORE] Subscribing to state channel: ${stateChannel}`);
|
|
4902
|
+
this.httpManagerService.subscribeToChannel(stateChannel);
|
|
4903
|
+
}
|
|
4904
|
+
// Process queued wsCommunication calls when connection becomes true
|
|
4905
|
+
if (HTTPManagerStateService.wsCommunicationQueue.length > 0) {
|
|
4906
|
+
console.log(`🔄 Processing ${HTTPManagerStateService.wsCommunicationQueue.length} queued WS messages`);
|
|
4907
|
+
while (HTTPManagerStateService.wsCommunicationQueue.length > 0) {
|
|
4908
|
+
const queued = HTTPManagerStateService.wsCommunicationQueue.shift();
|
|
4909
|
+
this.sendWsCommunication(queued.method, queued.path);
|
|
4910
|
+
}
|
|
4870
4911
|
}
|
|
4871
4912
|
}
|
|
4872
4913
|
})), this.httpManagerService.messages$.pipe(tap((message) => {
|
|
4873
4914
|
if (!message)
|
|
4874
4915
|
return;
|
|
4916
|
+
// CRITICAL DEBUG: Log EVERY message received
|
|
4917
|
+
console.log('🔍 [STATE STORE] Received WebSocket message:', {
|
|
4918
|
+
type: message.type,
|
|
4919
|
+
channel: message.channel,
|
|
4920
|
+
sessionId: message.data?.sessionId?.id || message.sessionId?.id,
|
|
4921
|
+
content: message.content || message.data?.content,
|
|
4922
|
+
timestamp: new Date().toISOString()
|
|
4923
|
+
});
|
|
4875
4924
|
// Add message to messages array
|
|
4876
4925
|
const currentMessages = this.messages.value;
|
|
4877
4926
|
this.messages.next([...currentMessages, message]);
|
|
@@ -4893,15 +4942,34 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
4893
4942
|
console.log('💬 Channels:', message.channels);
|
|
4894
4943
|
console.log('🔍 channelsList received, checking connection status...');
|
|
4895
4944
|
console.log('🔍 WebSocket connected:', WebSocketManagerService.isConnected());
|
|
4945
|
+
// Extract channel names from metadata objects (new format) or use strings directly (old format)
|
|
4946
|
+
const channelNames = message.channels.map((c) => {
|
|
4947
|
+
// Handle new format: {name: string, canSubscribe: boolean}
|
|
4948
|
+
if (typeof c === 'object' && c.name) {
|
|
4949
|
+
return c.name;
|
|
4950
|
+
}
|
|
4951
|
+
// Handle old format: string
|
|
4952
|
+
return c;
|
|
4953
|
+
});
|
|
4896
4954
|
// Auto-subscribe to all channels from the list
|
|
4897
|
-
if (
|
|
4898
|
-
console.log('📥 Auto-subscribing to',
|
|
4899
|
-
this.subscribeToChannels(
|
|
4955
|
+
if (channelNames && channelNames.length > 0) {
|
|
4956
|
+
console.log('📥 Auto-subscribing to', channelNames.length, 'channel(s)');
|
|
4957
|
+
this.subscribeToChannels(channelNames);
|
|
4900
4958
|
}
|
|
4901
4959
|
else {
|
|
4902
4960
|
console.log('⚠️ No channels to subscribe to');
|
|
4903
4961
|
}
|
|
4904
|
-
this.channels.next(
|
|
4962
|
+
this.channels.next(channelNames);
|
|
4963
|
+
break;
|
|
4964
|
+
case 'success':
|
|
4965
|
+
// Success messages - check for subscription confirmation
|
|
4966
|
+
console.log(`✅ Success: ${message.message}`);
|
|
4967
|
+
if (message.message?.includes('Subscribed to channel:')) {
|
|
4968
|
+
// Extract channel name from message: "Subscribed to channel: PUB-chat"
|
|
4969
|
+
const channelName = message.message.split('Subscribed to channel:')[1]?.trim();
|
|
4970
|
+
console.log(`✅ Subscription confirmed for channel: ${channelName}`);
|
|
4971
|
+
WebSocketManagerService.addSubscribedChannel(channelName);
|
|
4972
|
+
}
|
|
4905
4973
|
break;
|
|
4906
4974
|
case 'subscribed':
|
|
4907
4975
|
console.log(`✅ Subscription confirmed: ${message.channel}`);
|
|
@@ -4915,24 +4983,34 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
4915
4983
|
// Already subscribed or other info messages
|
|
4916
4984
|
console.log(`ℹ️ Info: ${message.message}`);
|
|
4917
4985
|
// If it's an "Already subscribed" message, treat it as subscription confirmation
|
|
4918
|
-
if (message.message?.includes('Already subscribed')) {
|
|
4919
|
-
|
|
4920
|
-
|
|
4986
|
+
if (message.message?.includes('Already subscribed to channel:')) {
|
|
4987
|
+
// Extract channel name from message: "Already subscribed to channel: PUB-chat"
|
|
4988
|
+
const channelName = message.message.split('Already subscribed to channel:')[1]?.trim();
|
|
4989
|
+
console.log(`✅ Treating info as subscription confirmation for channel: ${channelName}`);
|
|
4990
|
+
WebSocketManagerService.addSubscribedChannel(channelName);
|
|
4921
4991
|
}
|
|
4922
4992
|
break;
|
|
4923
4993
|
case 'stateMangerMessage':
|
|
4924
|
-
//
|
|
4994
|
+
// CRITICAL DEBUG: Log channel comparison
|
|
4995
|
+
console.log('🔍 [STATE STORE] stateMangerMessage received:', {
|
|
4996
|
+
messageChannel: message.channel,
|
|
4997
|
+
ourChannel: this.apiOptions.ws?.id,
|
|
4998
|
+
channelsMatch: message.channel === this.apiOptions.ws?.id,
|
|
4999
|
+
senderSessionId: message.data.sessionId?.id,
|
|
5000
|
+
ourSessionId: this.ownSessionId
|
|
5001
|
+
});
|
|
5002
|
+
// Compare sender's session ID with our own sessionId
|
|
4925
5003
|
// message.data.sessionId is an object with 'id' property from server
|
|
4926
5004
|
const stateManagerSenderId = message.data.sessionId?.id || message.data.sessionId;
|
|
4927
|
-
console.log('🔍 State Manager: Sender
|
|
4928
|
-
if (stateManagerSenderId !== this.
|
|
5005
|
+
console.log('🔍 State Manager: Sender sessionId:', stateManagerSenderId, 'Own sessionId:', this.ownSessionId);
|
|
5006
|
+
if (stateManagerSenderId !== this.ownSessionId) {
|
|
4929
5007
|
console.log('💬 State Manager Message:', message.data);
|
|
4930
5008
|
console.log('📥 Fetching record with path:', message.data.content.path, 'method:', message.data.content.method);
|
|
4931
5009
|
this.userAction.next(message.data);
|
|
4932
5010
|
this.fetchRecord(RequestOptions.adapt({ path: message.data.content.path }), message.data.content.method);
|
|
4933
5011
|
}
|
|
4934
5012
|
else {
|
|
4935
|
-
console.log('⏭️ Skipping own message');
|
|
5013
|
+
console.log('⏭️ Skipping own message (sessionId match)');
|
|
4936
5014
|
}
|
|
4937
5015
|
break;
|
|
4938
5016
|
case 'channelMessage':
|
|
@@ -4940,7 +5018,7 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
4940
5018
|
// Structure: { type: 'channelMessage', messageId, channel, sessionId, content, timestamp }
|
|
4941
5019
|
// Skip messages from self
|
|
4942
5020
|
const senderSessionId = message.sessionId?.id || message.sessionId;
|
|
4943
|
-
if (senderSessionId === this.
|
|
5021
|
+
if (senderSessionId === this.ownSessionId) {
|
|
4944
5022
|
console.log('🔇 Skipping message from self (sessionId match)');
|
|
4945
5023
|
break;
|
|
4946
5024
|
}
|
|
@@ -5003,11 +5081,25 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
5003
5081
|
break;
|
|
5004
5082
|
case 'notificationChannelsList':
|
|
5005
5083
|
console.log('📢 Notification Channels (in-memory):', message.channels);
|
|
5006
|
-
|
|
5084
|
+
// Extract channel names from metadata objects (new format) or use strings directly (old format)
|
|
5085
|
+
const notifyChannelNames = message.channels.map((c) => {
|
|
5086
|
+
if (typeof c === 'object' && c.name) {
|
|
5087
|
+
return c.name;
|
|
5088
|
+
}
|
|
5089
|
+
return c;
|
|
5090
|
+
});
|
|
5091
|
+
this.notificationChannels.next(notifyChannelNames || []);
|
|
5007
5092
|
break;
|
|
5008
5093
|
case 'todaysNotificationChannelsList':
|
|
5009
5094
|
console.log('📢 Today\'s Notification Channels (from DB):', message.channels);
|
|
5010
|
-
|
|
5095
|
+
// Extract channel names from metadata objects (new format) or use strings directly (old format)
|
|
5096
|
+
const todaysNotifyChannelNames = message.channels.map((c) => {
|
|
5097
|
+
if (typeof c === 'object' && c.name) {
|
|
5098
|
+
return c.name;
|
|
5099
|
+
}
|
|
5100
|
+
return c;
|
|
5101
|
+
});
|
|
5102
|
+
this.todaysNotificationChannels.next(todaysNotifyChannelNames || []);
|
|
5011
5103
|
break;
|
|
5012
5104
|
case 'notificationSubscribed':
|
|
5013
5105
|
console.log(`📢 Notification subscription confirmed: ${message.channel}`, message.notifications);
|
|
@@ -5418,6 +5510,12 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
5418
5510
|
return this.stripChannelPrefix(channel);
|
|
5419
5511
|
}
|
|
5420
5512
|
setApiRequestOptions(apiOptions, dataType, database) {
|
|
5513
|
+
console.log('🔧 [STATE STORE] setApiRequestOptions called:', {
|
|
5514
|
+
hasWs: !!apiOptions?.ws,
|
|
5515
|
+
wsId: apiOptions?.ws?.id,
|
|
5516
|
+
wsServer: apiOptions?.ws?.wsServer,
|
|
5517
|
+
path: apiOptions?.path
|
|
5518
|
+
});
|
|
5421
5519
|
this.apiOptions = ApiRequest.adapt(apiOptions);
|
|
5422
5520
|
this.dataType = (dataType) ? dataType : DataType.ARRAY;
|
|
5423
5521
|
// Only update database options if a database parameter is explicitly provided
|
|
@@ -5429,8 +5527,24 @@ class HTTPManagerStateService extends ComponentStore {
|
|
|
5429
5527
|
// Auto-prefix channel ID for private state manager channels
|
|
5430
5528
|
// This ensures state manager channels are separate from user-defined channels
|
|
5431
5529
|
this.apiOptions.ws.id = this.prefixChannel(this.apiOptions.ws.id, ChannelType.STATE);
|
|
5432
|
-
console.log(`🔒 Private state channel: ${this.apiOptions.ws.id}`);
|
|
5433
|
-
//
|
|
5530
|
+
console.log(`🔒 Private state channel configured: ${this.apiOptions.ws.id}`);
|
|
5531
|
+
// Store our own sessionId for filtering incoming messages
|
|
5532
|
+
this.ownSessionId = sessionStorage.getItem('WSID') || null;
|
|
5533
|
+
console.log(`🆔 Stored own sessionId for message filtering: ${this.ownSessionId}`);
|
|
5534
|
+
console.log(`🔍 [STATE STORE] WebSocket configuration:`, {
|
|
5535
|
+
channelId: this.apiOptions.ws.id,
|
|
5536
|
+
wsServer: this.apiOptions.ws.wsServer,
|
|
5537
|
+
sessionId: this.ownSessionId,
|
|
5538
|
+
path: this.apiOptions.path
|
|
5539
|
+
});
|
|
5540
|
+
// Initialize WebSocket connection if not already connected
|
|
5541
|
+
console.log('🔌 [STATE STORE] Checking WebSocket connection status...');
|
|
5542
|
+
console.log('🔌 [STATE STORE] Is connected?', WebSocketManagerService.isConnected());
|
|
5543
|
+
if (!WebSocketManagerService.isConnected()) {
|
|
5544
|
+
console.log('🔌 [STATE STORE] WebSocket not connected, will initialize via effect');
|
|
5545
|
+
// initWS is an effect that triggers when setApiRequestOptions is called with ws config
|
|
5546
|
+
// The effect is already triggered by calling setApiRequestOptions above
|
|
5547
|
+
}
|
|
5434
5548
|
if (this.apiOptions.ws?.retry) {
|
|
5435
5549
|
this.maxRetries = this.apiOptions.ws.retry.times || 3;
|
|
5436
5550
|
this.retryDelay = (this.apiOptions.ws.retry.delay && this.apiOptions.ws.retry.delay * 1000) || 5 * 1000;
|
|
@@ -6353,6 +6467,374 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
6353
6467
|
}]
|
|
6354
6468
|
}] });
|
|
6355
6469
|
|
|
6470
|
+
let ClientInfo$2 = class ClientInfo {
|
|
6471
|
+
constructor(domain = '', service = '', id = 0, name = '') {
|
|
6472
|
+
this.domain = domain;
|
|
6473
|
+
this.service = service;
|
|
6474
|
+
this.id = id;
|
|
6475
|
+
this.name = name;
|
|
6476
|
+
}
|
|
6477
|
+
static adapt(item) {
|
|
6478
|
+
return new ClientInfo(item?.domain, item?.service, item?.id, (item?.first_name || item?.last_name) ? `${item?.first_name} ${item?.last_name}` : '');
|
|
6479
|
+
}
|
|
6480
|
+
};
|
|
6481
|
+
|
|
6482
|
+
let ClientInfoMapper$2 = class ClientInfoMapper {
|
|
6483
|
+
constructor(id = 0, first_name = '', last_name = '', email = '') {
|
|
6484
|
+
this.id = id;
|
|
6485
|
+
this.first_name = first_name;
|
|
6486
|
+
this.last_name = last_name;
|
|
6487
|
+
this.email = email;
|
|
6488
|
+
}
|
|
6489
|
+
static adapt(item) {
|
|
6490
|
+
const first_name = (item?.name) ? item.name.split(' ')[0] : '';
|
|
6491
|
+
const last_name = (item?.name) ? item.name.split(' ')[1] : '';
|
|
6492
|
+
return new ClientInfoMapper(item?.id, first_name, last_name, item?.email);
|
|
6493
|
+
}
|
|
6494
|
+
};
|
|
6495
|
+
|
|
6496
|
+
let AIPrompt$2 = class AIPrompt {
|
|
6497
|
+
constructor(response = '') {
|
|
6498
|
+
this.response = response;
|
|
6499
|
+
}
|
|
6500
|
+
static adapt(item) {
|
|
6501
|
+
return new AIPrompt(item?.response);
|
|
6502
|
+
}
|
|
6503
|
+
};
|
|
6504
|
+
|
|
6505
|
+
class RequestManagerBasicDemoComponent {
|
|
6506
|
+
// Dynamic columns based on data structure
|
|
6507
|
+
getColumnsFromData(data) {
|
|
6508
|
+
if (!data || data.length === 0) {
|
|
6509
|
+
return [];
|
|
6510
|
+
}
|
|
6511
|
+
const firstRecord = data[0];
|
|
6512
|
+
if (!firstRecord || typeof firstRecord !== 'object') {
|
|
6513
|
+
return [];
|
|
6514
|
+
}
|
|
6515
|
+
// Extract all keys from the first record, excluding null/undefined
|
|
6516
|
+
return Object.keys(firstRecord).filter(key => firstRecord[key] !== null && firstRecord[key] !== undefined);
|
|
6517
|
+
}
|
|
6518
|
+
// Update displayed columns when data changes
|
|
6519
|
+
updateDisplayedColumns(data) {
|
|
6520
|
+
this.displayedColumns = this.getColumnsFromData(data);
|
|
6521
|
+
console.log('[DEMO] Updated columns:', this.displayedColumns);
|
|
6522
|
+
}
|
|
6523
|
+
// Helper to check if value is an object
|
|
6524
|
+
isObject(value) {
|
|
6525
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
6526
|
+
}
|
|
6527
|
+
get retry() {
|
|
6528
|
+
return this.requestForm.get('retry')?.value;
|
|
6529
|
+
}
|
|
6530
|
+
get headers() {
|
|
6531
|
+
return this.requestForm.get('headers');
|
|
6532
|
+
}
|
|
6533
|
+
get isValid() {
|
|
6534
|
+
this.requestForm.markAllAsTouched();
|
|
6535
|
+
return this.requestForm.valid;
|
|
6536
|
+
}
|
|
6537
|
+
constructor() {
|
|
6538
|
+
this.server = 'http://localhost:8080';
|
|
6539
|
+
this.displayedColumns = [];
|
|
6540
|
+
this.fb = inject(FormBuilder);
|
|
6541
|
+
this.toastMessage = inject(ToastMessageDisplayService);
|
|
6542
|
+
this.questionControl = this.fb.control("", [Validators.required]);
|
|
6543
|
+
this.httpManagerService = inject(HTTPManagerService);
|
|
6544
|
+
this.isPending$ = this.httpManagerService.isPending$;
|
|
6545
|
+
this.countdown$ = this.httpManagerService.countdown$;
|
|
6546
|
+
this.GET_error$ = new BehaviorSubject('');
|
|
6547
|
+
this.POST_error$ = new BehaviorSubject('');
|
|
6548
|
+
this.PUT_error$ = new BehaviorSubject('');
|
|
6549
|
+
this.DELETE_error$ = new BehaviorSubject('');
|
|
6550
|
+
this.STREAM_error$ = new BehaviorSubject('');
|
|
6551
|
+
this.STREAM_AI_error$ = new BehaviorSubject('');
|
|
6552
|
+
this.requestParams = {
|
|
6553
|
+
GET: ApiRequest.adapt(),
|
|
6554
|
+
POST: ApiRequest.adapt(),
|
|
6555
|
+
PUT: ApiRequest.adapt(),
|
|
6556
|
+
DELETE: ApiRequest.adapt(),
|
|
6557
|
+
STREAM: ApiRequest.adapt(),
|
|
6558
|
+
};
|
|
6559
|
+
this.streamTypes = [
|
|
6560
|
+
{ id: 'JSON', value: 'json' },
|
|
6561
|
+
{ id: 'NDJSON', value: 'ndjson' },
|
|
6562
|
+
{ id: 'AI Streaming', value: 'ai_streaming' },
|
|
6563
|
+
{ id: 'Event Stream', value: 'event_stream' },
|
|
6564
|
+
{ id: 'Auto', value: 'auto' },
|
|
6565
|
+
];
|
|
6566
|
+
this.streamType = 'Auto';
|
|
6567
|
+
this.downloadRequest = ApiRequest.adapt({
|
|
6568
|
+
server: 'assets/images',
|
|
6569
|
+
path: ['me.jpg'],
|
|
6570
|
+
saveAs: 'john.jpg', // Optional
|
|
6571
|
+
headers: { 'Authorization': 'Bearer Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InJ0c0ZULWItN0x1WTdEVlllU05LY0lKN1ZuYyJ9.eyJhdWQiOiI4ODhhMzFjZS01OWQzLTRhMTItYTU5Ni04ZDYyZjY0MWI1MDUiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vNmY0MTE5ODItY2YyMy00ZTQ1LTk0NDktNGI2MDdiN2E4OGVjL3YyLjAiLCJpYXQiOjE3NjU4MzQxNDksIm5iZiI6MTc2NTgzNDE0OSwiZXhwIjoxNzY1ODM5NzQ2LCJhY2N0IjowLCJhaW8iOiJBV1FBbS84YUFBQUFVUEZOYUhSZkZ4ZVkwRWthcFBzdWZaNG1QYXV6YVRoODRRZUtQRzkrbEFIVE5XanVwQy9ZSjEwK2IrMktWVlJSSXZRNFpwS2xyWTFGd0xRZmtXOTNLbkRNckxSeEMzWTVOOGlQREZ4b1liZExucW1QL1N1ZE1pRW1Va05tTklSWSIsImF6cCI6Ijg4OGEzMWNlLTU5ZDMtNGExMi1hNTk2LThkNjJmNjQxYjUwNSIsImF6cGFjciI6IjAiLCJncm91cHMiOlsiZWE2ZDk5YjAtNDgyNC00MDU0LTk0MTQtZDNhOTZkZDA3MjRiIl0sIm5hbWUiOiJHVExDIEFwcCBTdXBlciBBZG1pbiIsIm9pZCI6IjI2NzUxY2I2LWNlMDEtNDMzMC05OTc0LWZjMzgxMjQ3YTEzYyIsInByZWZlcnJlZF91c2VybmFtZSI6Imd0bGMwMDNAZ3RsYy5jYSIsInJoIjoiMS5BUzRBZ2hsQmJ5UFBSVTZVU1V0Z2UzcUk3TTR4aW9qVFdSSktwWmFOWXZaQnRRVm1BYTR1QUEuIiwicm9sZXMiOlsiZ3RsYy5hY2wuc3VwZXJhZG1pbiJdLCJzY3AiOiJHVExDX0FwcGxpY2F0aW9uIiwic2lkIjoiMDAzZjVhZDktZmJmNi1jZTkyLTg3MjItMWEzNDExYjMzMDJiIiwic3ViIjoiS0RYQ3drVlhUbTRUUmcwaTkxbXZDZzgtQ29uLXpWbk5FQ2Y5LVg0dkpzUSIsInRpZCI6IjZmNDExOTgyLWNmMjMtNGU0NS05NDQ5LTRiNjA3YjdhODhlYyIsInVwbiI6Imd0bGMwMDNAZ3RsYy5jYSIsInV0aSI6InpBaTJoMmpVUUVHbVY0RTJXMWxqQUEiLCJ2ZXIiOiIyLjAiLCJ3aWRzIjpbImI3OWZiZjRkLTNlZjktNDY4OS04MTQzLTc2YjE5NGU4NTUwOSJdLCJ4bXNfZnRkIjoiUDJraW1ZM0tqc1BTRndxNHlZdmJ0bGhrSUk3d2tXNmFpcW1FQ1BaNEdja0JkWE5sWVhOMExXUnpiWE0ifQ.LzppoggMm27smSAy9SamtPN95vCzdELCAfhtOj5n_T_H6g9xCmNRLS9FaUFQMau6Qvl0lROKl7WDklTswLFkfxbIxCBWtXdL-LTqT5cDURSJAll8vC3zlN3Hg9pAFBUVZFRolt6Z7LvPdI3pvUOQs0yFwVzp9k6cLF8aemKdwKQrMX3XXua1MfBWZcqQ4WiBVNmKh8w6yQB35I4u5WqdFnu33nUGb-kvc18SOpoUfiJnlV-PudaEzFXdU3CjAaMEcuPFv5xLwWJKuhU73dNH4EyQDFMVGtcIHNnieOfiY_nK2_0-5DM6aI40UIRK6Bt-HmMQpnbhLps5y3ep6Z7RNw' } // Optional
|
|
6572
|
+
});
|
|
6573
|
+
// downloadRequest = ApiRequest.adapt({
|
|
6574
|
+
// server: 'oidc/ai/file'
|
|
6575
|
+
// })
|
|
6576
|
+
this.sampleClientData = {
|
|
6577
|
+
id: 0,
|
|
6578
|
+
name: "Old School Dates",
|
|
6579
|
+
domain: "osd.com",
|
|
6580
|
+
service: "osd",
|
|
6581
|
+
spiffe: "osd.com/osd",
|
|
6582
|
+
secret: "SMOPECXP-OS4P-USOG-X2II-3XMD1FQDR3IJX",
|
|
6583
|
+
created: 1693003138,
|
|
6584
|
+
modified: 1693003138,
|
|
6585
|
+
icon: "",
|
|
6586
|
+
imageFile: "",
|
|
6587
|
+
email: "wavecoders@gmail.com"
|
|
6588
|
+
};
|
|
6589
|
+
this.requestForm = this.fb.group({
|
|
6590
|
+
path: this.fb.control("ai/"),
|
|
6591
|
+
headers: this.fb.array([]),
|
|
6592
|
+
adapter: [null],
|
|
6593
|
+
mapper: [null],
|
|
6594
|
+
retry: this.fb.group({
|
|
6595
|
+
times: [3],
|
|
6596
|
+
delay: [3],
|
|
6597
|
+
}),
|
|
6598
|
+
polling: [3],
|
|
6599
|
+
});
|
|
6600
|
+
this.AIType = 0;
|
|
6601
|
+
this.sampleAdaptors = [
|
|
6602
|
+
{ label: "ClientInfo Basic", value: ClientInfo$2.adapt },
|
|
6603
|
+
{ label: "AI Prompt", value: AIPrompt$2.adapt },
|
|
6604
|
+
];
|
|
6605
|
+
this.sampleMappers = [
|
|
6606
|
+
{ label: "Mapper Basic", value: ClientInfoMapper$2.adapt },
|
|
6607
|
+
{ label: "AI Prompt", value: AIPrompt$2.adapt },
|
|
6608
|
+
];
|
|
6609
|
+
this.hasId = (arr) => {
|
|
6610
|
+
if (arr.length === 0)
|
|
6611
|
+
return false;
|
|
6612
|
+
return !isNaN(arr[arr.length - 1]);
|
|
6613
|
+
};
|
|
6614
|
+
this.props = (adapter) => {
|
|
6615
|
+
return (adapter) ? adapter() : null;
|
|
6616
|
+
};
|
|
6617
|
+
// server = `http://sample-endpoint/as/authorization.oauth2`
|
|
6618
|
+
this.arrayObjectsToObjects = (arr) => {
|
|
6619
|
+
return Array.isArray(arr) ? arr.reduce((obj, item) => Object.assign(obj, { [item.key]: item.value }), {}) : {};
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
ngOnInit() {
|
|
6623
|
+
// const reqGet2 = ApiRequest.adapt({
|
|
6624
|
+
// server,
|
|
6625
|
+
// path: ['clients'],
|
|
6626
|
+
// headers: { authentication: "Bearer <KEY>" },
|
|
6627
|
+
// adapter: ClientInfo,
|
|
6628
|
+
// dataType: DataType.OBJECT,
|
|
6629
|
+
// // concurrent: false,
|
|
6630
|
+
// // polling: 3, //seconds
|
|
6631
|
+
// })
|
|
6632
|
+
// const req2 = [1024,1025,1026].map(item => {
|
|
6633
|
+
// return this.httpManagerService.getRequest<ClientInfo[]>(reqGet2, [item])
|
|
6634
|
+
// .pipe(
|
|
6635
|
+
// catchError(error => {
|
|
6636
|
+
// return throwError(() => new Error(error.error.message))
|
|
6637
|
+
// })
|
|
6638
|
+
// )
|
|
6639
|
+
// })
|
|
6640
|
+
// forkJoin(req2)
|
|
6641
|
+
// .subscribe(res => console.log(res))
|
|
6642
|
+
}
|
|
6643
|
+
onStreamType(type) {
|
|
6644
|
+
this.streamType = type;
|
|
6645
|
+
}
|
|
6646
|
+
addHeader() {
|
|
6647
|
+
const header = this.fb.group({
|
|
6648
|
+
key: ['', Validators.required],
|
|
6649
|
+
value: ['']
|
|
6650
|
+
});
|
|
6651
|
+
this.headers.push(header);
|
|
6652
|
+
}
|
|
6653
|
+
removeHeader(index) {
|
|
6654
|
+
this.headers.removeAt(index);
|
|
6655
|
+
}
|
|
6656
|
+
compileRequest() {
|
|
6657
|
+
const requestParams = this.requestForm.value;
|
|
6658
|
+
requestParams.headers = this.arrayObjectsToObjects(requestParams.headers || []);
|
|
6659
|
+
const pathReq = (requestParams.path === "") ? [] : (requestParams.path || "").split("/");
|
|
6660
|
+
if (!this.pollingState.checked)
|
|
6661
|
+
requestParams.polling = 0;
|
|
6662
|
+
if (!this.failedState.checked) {
|
|
6663
|
+
requestParams.retry = { times: 0, delay: 0 };
|
|
6664
|
+
}
|
|
6665
|
+
const apiOptions = ApiRequest.adapt(requestParams);
|
|
6666
|
+
apiOptions.path = [];
|
|
6667
|
+
apiOptions.server = this.server;
|
|
6668
|
+
apiOptions.adapter = this.adapter;
|
|
6669
|
+
apiOptions.mapper = this.mapper;
|
|
6670
|
+
return { apiOptions: apiOptions, path: pathReq };
|
|
6671
|
+
}
|
|
6672
|
+
onGetRequest() {
|
|
6673
|
+
if (!this.isValid)
|
|
6674
|
+
return;
|
|
6675
|
+
const reqParams = this.compileRequest();
|
|
6676
|
+
this.requestParams.GET = reqParams.apiOptions;
|
|
6677
|
+
this.GET$ = EMPTY; //Cancels Previous
|
|
6678
|
+
this.GET_error$.next('');
|
|
6679
|
+
this.GET$ = this.httpManagerService.getRequest({ ...reqParams.apiOptions }, reqParams.path)
|
|
6680
|
+
.pipe(
|
|
6681
|
+
// tap((data) => console.log("API GET response", data)),
|
|
6682
|
+
catchError(error => {
|
|
6683
|
+
return throwError(() => this.errorHandling(error, 'GET'));
|
|
6684
|
+
}));
|
|
6685
|
+
}
|
|
6686
|
+
onCreateRequest() {
|
|
6687
|
+
if (!this.isValid)
|
|
6688
|
+
return;
|
|
6689
|
+
const reqParams = this.compileRequest();
|
|
6690
|
+
this.requestParams.POST = reqParams.apiOptions;
|
|
6691
|
+
this.POST$ = EMPTY; //Cancels Previous
|
|
6692
|
+
this.POST_error$.next('');
|
|
6693
|
+
console.log("POST", this.sampleClientData);
|
|
6694
|
+
console.log("POST", reqParams.apiOptions);
|
|
6695
|
+
console.log("POST", reqParams.path);
|
|
6696
|
+
this.POST$ = this.httpManagerService.postRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
|
|
6697
|
+
.pipe(
|
|
6698
|
+
// tap((data) => console.log("API POST response", data)),
|
|
6699
|
+
catchError(error => {
|
|
6700
|
+
return throwError(() => this.errorHandling(error, 'POST'));
|
|
6701
|
+
}));
|
|
6702
|
+
}
|
|
6703
|
+
onUpdateRequest() {
|
|
6704
|
+
if (!this.isValid)
|
|
6705
|
+
return;
|
|
6706
|
+
const reqParams = this.compileRequest();
|
|
6707
|
+
if (!this.hasId(reqParams.path)) {
|
|
6708
|
+
console.log("Missing ID");
|
|
6709
|
+
return;
|
|
6710
|
+
}
|
|
6711
|
+
this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
|
|
6712
|
+
this.requestParams.PUT = reqParams.apiOptions;
|
|
6713
|
+
this.PUT$ = EMPTY; //Cancels Previous
|
|
6714
|
+
this.PUT_error$.next('');
|
|
6715
|
+
this.PUT$ = this.httpManagerService.putRequest(this.sampleClientData, reqParams.apiOptions, reqParams.path)
|
|
6716
|
+
.pipe(
|
|
6717
|
+
// tap((data) => console.log("API PUT response", data)),
|
|
6718
|
+
catchError(error => {
|
|
6719
|
+
return throwError(() => this.errorHandling(error, 'PUT'));
|
|
6720
|
+
}));
|
|
6721
|
+
}
|
|
6722
|
+
onDeleteRequest() {
|
|
6723
|
+
if (!this.isValid)
|
|
6724
|
+
return;
|
|
6725
|
+
const reqParams = this.compileRequest();
|
|
6726
|
+
this.requestParams.DELETE = reqParams.apiOptions;
|
|
6727
|
+
if (!this.hasId(reqParams.path)) {
|
|
6728
|
+
console.log("Missing ID");
|
|
6729
|
+
return;
|
|
6730
|
+
}
|
|
6731
|
+
this.sampleClientData.id = parseInt(reqParams.path[reqParams.path.length - 1]);
|
|
6732
|
+
this.requestParams.DELETE = reqParams.apiOptions;
|
|
6733
|
+
this.DELETE$ = EMPTY; //Cancels Previous
|
|
6734
|
+
this.DELETE_error$.next('');
|
|
6735
|
+
this.DELETE$ = this.httpManagerService.deleteRequest(reqParams.apiOptions, reqParams.path)
|
|
6736
|
+
.pipe(
|
|
6737
|
+
// tap((data) => console.log("API DELETE response", data)),
|
|
6738
|
+
catchError(error => {
|
|
6739
|
+
return throwError(() => this.errorHandling(error, 'DELETE'));
|
|
6740
|
+
}));
|
|
6741
|
+
}
|
|
6742
|
+
onStreamPostRequest() {
|
|
6743
|
+
if (!this.isValid)
|
|
6744
|
+
return;
|
|
6745
|
+
const reqParams = this.compileRequest();
|
|
6746
|
+
let payload = {};
|
|
6747
|
+
let apiPath = reqParams.path;
|
|
6748
|
+
let apiOptions = reqParams.apiOptions;
|
|
6749
|
+
let responseMapper = (items) => items.response;
|
|
6750
|
+
if (this.AIType === 0) {
|
|
6751
|
+
// API request
|
|
6752
|
+
payload = { prompt: this.questionControl.value };
|
|
6753
|
+
}
|
|
6754
|
+
else {
|
|
6755
|
+
// Local Ollama request
|
|
6756
|
+
apiOptions.server = "api";
|
|
6757
|
+
apiPath = ["generate"];
|
|
6758
|
+
apiOptions.stream = true;
|
|
6759
|
+
apiOptions.streamType = this.streamType;
|
|
6760
|
+
payload = {
|
|
6761
|
+
model: "phi3:latest",
|
|
6762
|
+
prompt: this.questionControl.value,
|
|
6763
|
+
stream: true,
|
|
6764
|
+
};
|
|
6765
|
+
responseMapper = (items) => items.map((word) => word.response).flat().join('');
|
|
6766
|
+
}
|
|
6767
|
+
this.requestParams.STREAM = apiOptions;
|
|
6768
|
+
this.STREAM_AI$ = EMPTY;
|
|
6769
|
+
this.STREAM_AI_error$.next('');
|
|
6770
|
+
this.STREAM_AI$ = this.httpManagerService.postRequest(payload, apiOptions, apiPath).pipe(map(responseMapper), tap(() => this.questionControl.reset()), catchError(error => throwError(() => this.errorHandling(error, 'STREAM'))));
|
|
6771
|
+
}
|
|
6772
|
+
onStreamRequest() {
|
|
6773
|
+
if (!this.isValid)
|
|
6774
|
+
return;
|
|
6775
|
+
const reqParams = this.compileRequest();
|
|
6776
|
+
reqParams.apiOptions.stream = true;
|
|
6777
|
+
reqParams.apiOptions.streamType = StreamType.NDJSON;
|
|
6778
|
+
this.requestParams.GET = reqParams.apiOptions;
|
|
6779
|
+
this.STREAM$ = this.httpManagerService.getRequest(reqParams.apiOptions, reqParams.path)
|
|
6780
|
+
.pipe(tap((data) => {
|
|
6781
|
+
console.log("API STREAM response", data);
|
|
6782
|
+
if (data && data.length > 0) {
|
|
6783
|
+
this.updateDisplayedColumns(data);
|
|
6784
|
+
}
|
|
6785
|
+
}), catchError(error => {
|
|
6786
|
+
return throwError(() => this.errorHandling(error, 'STREAM'));
|
|
6787
|
+
}));
|
|
6788
|
+
}
|
|
6789
|
+
onDownloadCompleted() {
|
|
6790
|
+
const message = "Download Completed";
|
|
6791
|
+
const display = ToastDisplay.adapt({
|
|
6792
|
+
message,
|
|
6793
|
+
action: 'Ok',
|
|
6794
|
+
color: ToastColors.SUCCESS,
|
|
6795
|
+
icon: 'sentiment_satisfied_alt',
|
|
6796
|
+
});
|
|
6797
|
+
this.toastMessage.toastMessage(display);
|
|
6798
|
+
}
|
|
6799
|
+
onDownloadFailed(err) {
|
|
6800
|
+
const message = "Download Failed";
|
|
6801
|
+
const display = ToastDisplay.adapt({
|
|
6802
|
+
message,
|
|
6803
|
+
action: 'Ok',
|
|
6804
|
+
color: ToastColors.ERROR,
|
|
6805
|
+
icon: 'warning',
|
|
6806
|
+
});
|
|
6807
|
+
this.toastMessage.toastMessage(display);
|
|
6808
|
+
}
|
|
6809
|
+
errorHandling(err, type) {
|
|
6810
|
+
if (type === 'GET')
|
|
6811
|
+
this.GET_error$.next(err.message);
|
|
6812
|
+
if (type === 'POST')
|
|
6813
|
+
this.POST_error$.next(err.message);
|
|
6814
|
+
if (type === 'PUT')
|
|
6815
|
+
this.PUT_error$.next(err.message);
|
|
6816
|
+
if (type === 'DELETE')
|
|
6817
|
+
this.DELETE_error$.next(err.message);
|
|
6818
|
+
if (type === 'STREAM')
|
|
6819
|
+
this.STREAM_error$.next(err.message);
|
|
6820
|
+
}
|
|
6821
|
+
onSelectAIType(type) {
|
|
6822
|
+
this.AIType = type;
|
|
6823
|
+
}
|
|
6824
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerBasicDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6825
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerBasicDemoComponent, selector: "app-request-manager-basic-demo", providers: [HTTPManagerService], viewQueries: [{ propertyName: "failedState", first: true, predicate: ["failedState"], descendants: true, static: true }, { propertyName: "pollingState", first: true, predicate: ["pollingState"], descendants: true, static: true }], ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Basic Request Manager Setup\n </h2>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked && !(isPending$ | async)) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n @if ((GET$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n @if ((POST$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <h3>Include Record ID in the RestPath</h3>\n\n @if ((PUT$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n @if ((DELETE$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Records: {{ data.length }}\n </div>\n </div>\n }\n </div>\n\n</div>\n\n<div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n</div>\n\n<div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -@if (AIType === 1) {\n <span>STREAMING</span>\n } POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n @if (AIType === 0) {\n <span>Server</span>\n } @else {\n Local\n }\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n @if ((STREAM_AI_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n @if (AIType === 1) {\n <div style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n } @else {\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n }\n\n <div>\n @if ((STREAM_AI$ | async); as data) {\n <div style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n }\n </div>\n\n</div>\n\n<div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n</div>\n\n<div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n @if (disable.checked) {\n <span>\n Enable\n </span>\n } @else {\n Disable\n }\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n</div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { 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: i3.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { 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: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i10.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i11.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "component", type: i12.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: FileDownloaderComponent, selector: "app-file-downloader", inputs: ["delayError", "apiRequest", "displayErrorMessage", "saveFileAs", "labels", "disabled"], outputs: ["completed", "failed"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] }); }
|
|
6826
|
+
}
|
|
6827
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerBasicDemoComponent, decorators: [{
|
|
6828
|
+
type: Component,
|
|
6829
|
+
args: [{ selector: 'app-request-manager-basic-demo', providers: [HTTPManagerService], standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n HTTP Basic Request Manager Setup\n </h2>\n\n <div style=\"margin-bottom: 1rem; margin-top: 2rem;\">\n @if ((isPending$ | async)) {\n <mat-progress-bar mode=\"indeterminate\"\n ></mat-progress-bar>\n }\n @if (pollingState.checked && !(isPending$ | async)) {\n <mat-progress-bar mode=\"determinate\"\n [value]=\"(this.countdown$ | async)\"\n ></mat-progress-bar>\n }\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">GET Request</h2>\n <div>\n <button mat-raised-button (click)=\"onGetRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((GET_error$ | async); as get_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ get_error }}</mat-error>\n </div>\n }\n\n @if ((GET$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(GET$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">POST Request</h2>\n <div>\n <button mat-raised-button (click)=\"onCreateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((POST_error$ | async); as post_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ post_error }}</mat-error>\n </div>\n }\n\n @if ((POST$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(POST$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">PUT Request</h2>\n <div>\n <button mat-raised-button (click)=\"onUpdateRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n @if ((PUT_error$ | async); as put_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ put_error }}</mat-error>\n </div>\n }\n\n <h3>Include Record ID in the RestPath</h3>\n\n @if ((PUT$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(PUT$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">DELETE Request</h2>\n <div>\n <button mat-raised-button (click)=\"onDeleteRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <h3>Include Record ID in the RestPath</h3>\n\n @if ((DELETE_error$ | async); as delete_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ delete_error }}</mat-error>\n </div>\n }\n\n @if ((DELETE$ | async); as dataRecord) {\n <div style=\"margin-top: 1rem;\">\n <!-- <div [innerHTML]=\"(DELETE$ | async) | jsonv\"></div> -->\n {{ dataRecord | json }}\n </div>\n }\n\n </div>\n\n <div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n </div>\n\n <div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1\">Streaming GET Request</h2>\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n <div style=\"display: flex; gap: 1rem; align-items: center;\">\n {{ streamType }}\n <button mat-icon-button [matMenuTriggerFor]=\"menu\">\n <mat-icon>data_usage</mat-icon>\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item *ngFor=\"let item of streamTypes\" (click)=\"onStreamType(item.id)\">{{ item.value }}</button>\n </mat-menu>\n <button mat-raised-button (click)=\"onStreamRequest()\" class=\"btn\">Request</button>\n </div>\n </div>\n\n <!-- <div *ngIf=\"(STREAM_error$ | async) as stream_error\" style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div> -->\n\n <div style=\"margin-top: 1rem;\">\n @if ((STREAM$ | async); as data) {\n <div class=\"container\">\n <table mat-table [dataSource]=\"data\" class=\"mat-elevation-z8\">\n\n <!-- Dynamic columns -->\n <ng-container *ngFor=\"let column of displayedColumns\" [matColumnDef]=\"column\">\n <th mat-header-cell *matHeaderCellDef> {{ column | titlecase }} </th>\n <td mat-cell *matCellDef=\"let element\">\n @if (isObject(element[column]); as objValue) {\n <pre style=\"margin: 0; font-size: 0.8em; white-space: pre-wrap;\">{{ objValue | json }}</pre>\n } @else {\n {{ element[column] }}\n }\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n\n <!-- Debug info -->\n <div style=\"margin-top: 1rem; font-size: 0.8em; color: #666;\">\n Columns: {{ displayedColumns.join(', ') }} | Records: {{ data.length }}\n </div>\n </div>\n }\n </div>\n\n</div>\n\n<div style=\"margin-top: 2rem\">\n <mat-divider></mat-divider>\n</div>\n\n<div style=\"margin-top: 2rem\">\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; padding-top: .5rem;\">AI -@if (AIType === 1) {\n <span>STREAMING</span>\n } POST Request</h2>\n <div style=\"display: flex; gap: 1rem;\">\n <button mat-raised-button [matMenuTriggerFor]=\"menu\" style=\"min-width: 120px;\">\n <mat-icon>lan</mat-icon>\n @if (AIType === 0) {\n <span>Server</span>\n } @else {\n Local\n }\n </button>\n <mat-menu #menu=\"matMenu\">\n <button mat-menu-item (click)=\"onSelectAIType(0)\">Server</button>\n <button mat-menu-item (click)=\"onSelectAIType(1)\">Local</button>\n </mat-menu>\n <div>\n <button mat-raised-button (click)=\"onStreamPostRequest()\" class=\"btn\">Ask Me</button>\n </div>\n </div>\n </div>\n\n <div style=\"display: flex;\">\n <mat-form-field appearance=\"outline\" style=\"flex:1\">\n <mat-label>Ask me a Question</mat-label>\n <textarea matInput placeholder=\"Why is the sky blue?\" [formControl]=\"questionControl\"></textarea>\n </mat-form-field>\n </div>\n\n @if ((STREAM_AI_error$ | async); as stream_error) {\n <div style=\"margin-top: .5rem;\">\n <mat-error>{{ stream_error }}</mat-error>\n </div>\n }\n\n @if (AIType === 1) {\n <div style=\"color: red;\">\n You must have Ollama active and the 'phi3:latest' model to use this feature.\n </div>\n } @else {\n <span style=\"color: gray;\">\n Define the RestPath to the API endpoint that will handle the AI request.\n Use: 'ai/chat' for server\n </span>\n }\n\n <div>\n @if ((STREAM_AI$ | async); as data) {\n <div style=\"margin-top: 1rem; font-size: 1.2rem; border-radius: 1rem; border: black 1px solid; padding: 2rem;\">\n <p style=\"margin-bottom: .5rem; white-space:pre-wrap; line-height: 1.6rem;\">{{data}}</p>\n </div>\n }\n </div>\n\n</div>\n\n<div style=\"margin-top: 1.5rem; margin-bottom: 1rem; line-height: 1.5rem;\">\n <mat-divider></mat-divider>\n</div>\n\n<div>\n <div style=\"display: flex;\">\n <h2 style=\"flex:1; margin-bottom: 0; padding-top: .5rem; display: flex;\">\n <div>\n Download File\n </div>\n <div style=\"flex:1; margin-left: 1rem;\">\n <mat-slide-toggle #disable>\n @if (disable.checked) {\n <span>\n Enable\n </span>\n } @else {\n Disable\n }\n </mat-slide-toggle>\n </div>\n </h2>\n <div>\n <app-file-downloader\n [disabled]=\"disable.checked\"\n [delayError]=\"3\"\n [apiRequest]=\"downloadRequest\"\n (completed)=\"onDownloadCompleted()\"\n (failed)=\"onDownloadFailed($event)\"\n ></app-file-downloader>\n </div>\n </div>\n</div>\n\n</div>\n", styles: [".btn{min-width:120px}.mat-mdc-row .mat-mdc-cell{border-bottom:1px solid transparent;border-top:1px solid transparent;cursor:pointer}.mat-mdc-row:hover .mat-mdc-cell{border-color:currentColor;background-color:#f5f5f5}.container{height:400px;overflow:auto}.box{padding:10px;border:1px solid #ccc}\n"] }]
|
|
6830
|
+
}], ctorParameters: () => [], propDecorators: { failedState: [{
|
|
6831
|
+
type: ViewChild,
|
|
6832
|
+
args: ["failedState", { static: true }]
|
|
6833
|
+
}], pollingState: [{
|
|
6834
|
+
type: ViewChild,
|
|
6835
|
+
args: ["pollingState", { static: true }]
|
|
6836
|
+
}] } });
|
|
6837
|
+
|
|
6356
6838
|
let ClientInfo$1 = class ClientInfo {
|
|
6357
6839
|
constructor(domain = '', service = '', id = 0, name = '') {
|
|
6358
6840
|
this.domain = domain;
|
|
@@ -7803,7 +8285,11 @@ class WsMessagingComponent {
|
|
|
7803
8285
|
});
|
|
7804
8286
|
// Subscribe to latest messages and show toast notification
|
|
7805
8287
|
this.latestCommunicationMessages$.pipe(filter$1(message => !!message), takeUntil$1(this.destroy$)).subscribe((message) => {
|
|
7806
|
-
|
|
8288
|
+
console.log('🔔 Toast notification triggered:', message);
|
|
8289
|
+
console.log(' message.content:', message.content);
|
|
8290
|
+
// Message structure has nested content: message.content.content.message
|
|
8291
|
+
const messageContent = message.content?.content?.message || message.content?.message || '';
|
|
8292
|
+
console.log(' Extracted messageContent:', messageContent);
|
|
7807
8293
|
this.toastService.toastMessage(ToastDisplay.adapt({
|
|
7808
8294
|
message: messageContent,
|
|
7809
8295
|
color: ToastColors.INFO,
|
|
@@ -7855,8 +8341,14 @@ class WsMessagingComponent {
|
|
|
7855
8341
|
/**
|
|
7856
8342
|
* Handle chip toggle for subscribe/unsubscribe
|
|
7857
8343
|
*/
|
|
7858
|
-
onChipToggle(
|
|
7859
|
-
|
|
8344
|
+
onChipToggle(event, channel) {
|
|
8345
|
+
const isSelected = event.selected;
|
|
8346
|
+
console.log('🎯 Chip toggle event:', {
|
|
8347
|
+
channel,
|
|
8348
|
+
isSelected,
|
|
8349
|
+
option: event.source
|
|
8350
|
+
});
|
|
8351
|
+
if (isSelected) {
|
|
7860
8352
|
this.onSubscribeToChannel(channel);
|
|
7861
8353
|
}
|
|
7862
8354
|
else {
|
|
@@ -7870,44 +8362,39 @@ class WsMessagingComponent {
|
|
|
7870
8362
|
isSubscribed(channel, subscribedChannels) {
|
|
7871
8363
|
return subscribedChannels.includes(channel);
|
|
7872
8364
|
}
|
|
7873
|
-
|
|
7874
|
-
* Handle chip click - toggle subscription state
|
|
7875
|
-
*/
|
|
7876
|
-
onChipClick(channel, subscribedChannels) {
|
|
7877
|
-
const isCurrentlySubscribed = this.isSubscribed(channel, subscribedChannels);
|
|
7878
|
-
if (isCurrentlySubscribed) {
|
|
7879
|
-
this.onUnsubscribeFromChannel(channel);
|
|
7880
|
-
}
|
|
7881
|
-
else {
|
|
7882
|
-
this.onSubscribeToChannel(channel);
|
|
7883
|
-
}
|
|
7884
|
-
}
|
|
7885
|
-
onSendMessage(user) {
|
|
8365
|
+
onSendMessage() {
|
|
7886
8366
|
this.messages.markAllAsTouched();
|
|
7887
8367
|
if (this.messages.invalid)
|
|
7888
8368
|
return;
|
|
7889
8369
|
const channelsToSend = this.selectedChannels.value;
|
|
7890
8370
|
if (channelsToSend.length === 0)
|
|
7891
8371
|
return;
|
|
7892
|
-
//
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
8372
|
+
// Get user from observable - subscribe once
|
|
8373
|
+
this.user$.pipe(take$1(1)).subscribe(user => {
|
|
8374
|
+
if (!user) {
|
|
8375
|
+
console.error('❌ No user found');
|
|
8376
|
+
return;
|
|
8377
|
+
}
|
|
8378
|
+
// Pass base channel names - service adds PUB- prefix
|
|
8379
|
+
const message = ChannelMessage.adapt({
|
|
8380
|
+
sessionId: {
|
|
8381
|
+
id: user.id,
|
|
8382
|
+
ldap: user.ldap,
|
|
8383
|
+
name: user.name,
|
|
8384
|
+
email: user.email,
|
|
8385
|
+
},
|
|
8386
|
+
content: { message: this.messages.value.content },
|
|
8387
|
+
});
|
|
8388
|
+
this.messageService.sendMessage(message, channelsToSend);
|
|
8389
|
+
this.content.reset();
|
|
7901
8390
|
});
|
|
7902
|
-
this.messageService.sendMessage(message, channelsToSend);
|
|
7903
|
-
this.content.reset();
|
|
7904
8391
|
}
|
|
7905
8392
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7906
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path" }, ngImport: i0, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel; let i = $index) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; flex-direction: column;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Message</mat-label>\n <textarea\n matInput placeholder=\"Type your message...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(
|
|
8393
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path" }, ngImport: i0, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n <!-- Debug: Show subscribed channels count -->\n <div style=\"font-size: 0.8rem; color: #666; margin-bottom: 0.5rem;\">\n Subscribed to {{ subscribedChannels.length }} channel(s): {{ subscribedChannels | json }}\n </div>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel; let i = $index) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; flex-direction: column;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Message</mat-label>\n <textarea\n matInput placeholder=\"Type your message...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button mat-raised-button color=\"primary\" (click)=\"onSendMessage()\"\n [disabled]=\"messages.invalid\">\n Send\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n </div>\n }\n }\n\n <!-- Channel List with Subscription Controls using Chips -->\n @if ((channels$ | async); as channels) {\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n <div style=\"padding: 1rem; background: #f5f5f5; border-radius: 4px; margin-top: 1rem;\">\n <strong>Subscribe to Channel(s)</strong>\n <div style=\"margin-top: 0.5rem;\">\n @if (channels.length === 0) {\n <div style=\"color: #666; font-style: italic;\">No channels available. Create one above.</div>\n } @else {\n <mat-chip-listbox\n aria-label=\"Channel selection\"\n [multiple]=\"true\">\n @for (channel of channels; track channel; let i = $index) {\n <mat-chip-option\n [selected]=\"isSubscribed(channel, subscribedChannels)\"\n (selectionChange)=\"onChipToggle($event, channel)\">\n {{ channel }}\n </mat-chip-option>\n }\n </mat-chip-listbox>\n }\n </div>\n </div>\n }\n }\n", styles: ["button[mat-raised-button],button[mat-stroked-button]{min-width:120px}mat-chip-option{min-width:120px;justify-content:center;border-radius:4px!important;padding:.25rem!important}\n"], dependencies: [{ kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: i5.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i3$1.MatChipListbox, selector: "mat-chip-listbox", inputs: ["multiple", "aria-orientation", "selectable", "compareWith", "required", "hideSingleSelectionIndicator", "value"], outputs: ["change"] }, { kind: "component", type: i3$1.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
|
|
7907
8394
|
}
|
|
7908
8395
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, decorators: [{
|
|
7909
8396
|
type: Component,
|
|
7910
|
-
args: [{ selector: 'app-ws-messaging', standalone: false, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel; let i = $index) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; flex-direction: column;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Message</mat-label>\n <textarea\n matInput placeholder=\"Type your message...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(
|
|
8397
|
+
args: [{ selector: 'app-ws-messaging', standalone: false, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n <!-- Debug: Show subscribed channels count -->\n <div style=\"font-size: 0.8rem; color: #666; margin-bottom: 0.5rem;\">\n Subscribed to {{ subscribedChannels.length }} channel(s): {{ subscribedChannels | json }}\n </div>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel; let i = $index) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; flex-direction: column;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Message</mat-label>\n <textarea\n matInput placeholder=\"Type your message...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button mat-raised-button color=\"primary\" (click)=\"onSendMessage()\"\n [disabled]=\"messages.invalid\">\n Send\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n </div>\n }\n }\n\n <!-- Channel List with Subscription Controls using Chips -->\n @if ((channels$ | async); as channels) {\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n <div style=\"padding: 1rem; background: #f5f5f5; border-radius: 4px; margin-top: 1rem;\">\n <strong>Subscribe to Channel(s)</strong>\n <div style=\"margin-top: 0.5rem;\">\n @if (channels.length === 0) {\n <div style=\"color: #666; font-style: italic;\">No channels available. Create one above.</div>\n } @else {\n <mat-chip-listbox\n aria-label=\"Channel selection\"\n [multiple]=\"true\">\n @for (channel of channels; track channel; let i = $index) {\n <mat-chip-option\n [selected]=\"isSubscribed(channel, subscribedChannels)\"\n (selectionChange)=\"onChipToggle($event, channel)\">\n {{ channel }}\n </mat-chip-option>\n }\n </mat-chip-listbox>\n }\n </div>\n </div>\n }\n }\n", styles: ["button[mat-raised-button],button[mat-stroked-button]{min-width:120px}mat-chip-option{min-width:120px;justify-content:center;border-radius:4px!important;padding:.25rem!important}\n"] }]
|
|
7911
8398
|
}], propDecorators: { server: [{
|
|
7912
8399
|
type: Input
|
|
7913
8400
|
}], wsServer: [{
|
|
@@ -8099,11 +8586,11 @@ class WsNotificationsComponent {
|
|
|
8099
8586
|
return new Date().toISOString().split('T')[0];
|
|
8100
8587
|
}
|
|
8101
8588
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsNotificationsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8102
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsNotificationsComponent, selector: "app-ws-notifications", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user" }, ngImport: i0, template: "<div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e8f5e9; border-radius: 4px;\">\n <strong>Create Notification Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input\n matInput\n [formControl]=\"newChannelName\"\n placeholder=\"Enter notification channel name\"\n (keydown.enter)=\"onCreateChannel()\"\n >\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; gap: 0.5rem;\">\n <button mat-raised-button color=\"accent\" (click)=\"onDefinePreviousChannels()\">\n Define Previous Channels\n </button>\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n </div>\n\n <!-- Channel Connection Section - Only shown when NOT connected -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (!isChannelConnected(subscribedChannels)) {\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Connect to Channel</strong>\n <div style=\"display: flex; gap: 1rem; margin-top: 0.5rem; flex-wrap: wrap; align-items: flex-start;\">\n <!-- Channel Selection (from today's DB data) -->\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\">\n <mat-label>Select Channel</mat-label>\n <mat-select [formControl]=\"selectedConnectionChannel\" placeholder=\"Select a notification channel\">\n @for (channel of (todaysNotificationChannels$ | async) || []; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n @empty {\n <mat-option disabled>No channels with data for today</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Date Filter -->\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\" [formGroup]=\"dateFilter\">\n <mat-label>Start Date</mat-label>\n <input matInput [matDatepicker]=\"startPicker\" formControlName=\"startDate\">\n <mat-datepicker-toggle matIconSuffix [for]=\"startPicker\"></mat-datepicker-toggle>\n <mat-datepicker #startPicker></mat-datepicker>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\" [formGroup]=\"dateFilter\">\n <mat-label>End Date</mat-label>\n <input matInput [matDatepicker]=\"endPicker\" formControlName=\"endDate\">\n <mat-datepicker-toggle matIconSuffix [for]=\"endPicker\"></mat-datepicker-toggle>\n <mat-datepicker #endPicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; gap: 0.5rem;\">\n <span style=\"flex:1\"></span>\n <button\n mat-raised-button color=\"primary\"\n (click)=\"onConnectToChannel()\"\n [disabled]=\"!selectedConnectionChannel.value\"\n >\n Connect\n </button>\n </div>\n </div>\n }\n }\n\n <!-- Send Notification Section -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div\n style=\"padding: 1rem; background: #fff8e1; border-radius: 4px;\"\n [formGroup]=\"notificationForm\"\n >\n <strong>Send Notification</strong>\n\n <!-- Notification Content -->\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Notification Message</mat-label>\n <textarea\n matInput placeholder=\"Type your notification...\"\n formControlName=\"content\"\n rows=\"2\"\n (keydown.enter)=\"onSendNotification(); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button\n mat-raised-button color=\"accent\"\n (click)=\"onSendNotification()\"\n [disabled]=\"notificationForm.invalid\"\n >\n Send Notification\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n <!-- Notifications Table - Only shown when connected -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (isChannelConnected(subscribedChannels)) {\n @if ((notificationMessages$ | async); as notifications) {\n <div>\n <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n <strong>Notifications ({{ notifications.length }})</strong>\n </div>\n\n @if (notifications.length > 0) {\n <div style=\"margin-top: 0.5rem; height: 400px; overflow: auto; border: 1px solid #888; border-radius: 4px;\">\n <table mat-table [dataSource]=\"notifications\" style=\"width: 100%;\">\n\n <!-- Date Column -->\n <ng-container matColumnDef=\"date\">\n <th mat-header-cell *matHeaderCellDef style=\"width: 180px;\">Date</th>\n <td mat-cell *matCellDef=\"let notification\">\n {{ formatTimestamp(notification.created) | date }}\n </td>\n </ng-container>\n\n <!-- User Column -->\n <ng-container matColumnDef=\"user\">\n <th mat-header-cell *matHeaderCellDef style=\"width: 150px;\">User</th>\n <td mat-cell *matCellDef=\"let notification\">\n <strong>{{ notification.user_name }}</strong>\n </td>\n </ng-container>\n\n <!-- Message Column -->\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef>Message</th>\n <td mat-cell *matCellDef=\"let notification\">\n <div class=\"pill\" [style.background-color]=\"notification.content.sessionId.color || '#000000'\">\n {{ notification.content?.message || notification.content }}\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n } @else {\n <div style=\"text-align: center; color: #666; padding: 2rem;\">\n <mat-icon style=\"font-size: 48px; height: 48px; width: 48px; opacity: 0.5;\">notifications_none</mat-icon>\n <p>No notifications yet. Waiting for notifications on this channel...</p>\n </div>\n }\n </div>\n\n <!-- Disconnect Button - Below the table -->\n <div style=\"display: flex; justify-content: right; padding: 1rem;\">\n <button mat-raised-button color=\"warn\" (click)=\"onDisconnectFromChannel()\">\n Disconnect from Channel\n </button>\n </div>\n }\n }\n }\n\n</div>\n", styles: [".colored-message{font-weight:500;padding:4px 8px;border-radius:4px;display:inline-block;max-width:100%;word-wrap:break-word}.colored-message.black-text{color:#000!important}.colored-message.white-text{color:#fff!important;text-shadow:0 0 2px rgba(0,0,0,.5)}td.mat-mdc-cell{padding:8px 12px}.pill{border-radius:1rem;padding:.5rem 1rem;color:#fff}\n"], dependencies: [{ kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i9$1.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i9$1.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i9$1.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] }); }
|
|
8589
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsNotificationsComponent, selector: "app-ws-notifications", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user" }, ngImport: i0, template: "<div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e8f5e9; border-radius: 4px;\">\n <strong>Create Notification Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input\n matInput\n [formControl]=\"newChannelName\"\n placeholder=\"Enter notification channel name\"\n (keydown.enter)=\"onCreateChannel()\"\n >\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; gap: 0.5rem;\">\n <!-- <button mat-raised-button color=\"accent\" (click)=\"onDefinePreviousChannels()\">\n Define Previous Channels\n </button> -->\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel 1\n </button>\n </div>\n </div>\n </div>\n\n <!-- Channel Connection Section - Only shown when NOT connected -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (!isChannelConnected(subscribedChannels)) {\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Connect to Channel</strong>\n <div style=\"display: flex; gap: 1rem; margin-top: 0.5rem; flex-wrap: wrap; align-items: flex-start;\">\n <!-- Channel Selection (from today's DB data) -->\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\">\n <mat-label>Select Channel</mat-label>\n <mat-select [formControl]=\"selectedConnectionChannel\" placeholder=\"Select a notification channel\">\n @for (channel of (todaysNotificationChannels$ | async) || []; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n @empty {\n <mat-option disabled>No channels with data for today</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Date Filter -->\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\" [formGroup]=\"dateFilter\">\n <mat-label>Start Date</mat-label>\n <input matInput [matDatepicker]=\"startPicker\" formControlName=\"startDate\">\n <mat-datepicker-toggle matIconSuffix [for]=\"startPicker\"></mat-datepicker-toggle>\n <mat-datepicker #startPicker></mat-datepicker>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\" [formGroup]=\"dateFilter\">\n <mat-label>End Date</mat-label>\n <input matInput [matDatepicker]=\"endPicker\" formControlName=\"endDate\">\n <mat-datepicker-toggle matIconSuffix [for]=\"endPicker\"></mat-datepicker-toggle>\n <mat-datepicker #endPicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; gap: 0.5rem;\">\n <span style=\"flex:1\"></span>\n <button\n mat-raised-button color=\"primary\"\n (click)=\"onConnectToChannel()\"\n [disabled]=\"!selectedConnectionChannel.value\"\n >\n Connect\n </button>\n </div>\n </div>\n }\n }\n\n <!-- Send Notification Section -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div\n style=\"padding: 1rem; background: #fff8e1; border-radius: 4px;\"\n [formGroup]=\"notificationForm\"\n >\n <strong>Send Notification</strong>\n\n <!-- Notification Content -->\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Notification Message</mat-label>\n <textarea\n matInput placeholder=\"Type your notification...\"\n formControlName=\"content\"\n rows=\"2\"\n (keydown.enter)=\"onSendNotification(); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button\n mat-raised-button color=\"accent\"\n (click)=\"onSendNotification()\"\n [disabled]=\"notificationForm.invalid\"\n >\n Send Notification\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n <!-- Notifications Table - Only shown when connected -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (isChannelConnected(subscribedChannels)) {\n @if ((notificationMessages$ | async); as notifications) {\n <div>\n <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n <strong>Notifications ({{ notifications.length }})</strong>\n </div>\n\n @if (notifications.length > 0) {\n <div style=\"margin-top: 0.5rem; height: 400px; overflow: auto; border: 1px solid #888; border-radius: 4px;\">\n <table mat-table [dataSource]=\"notifications\" style=\"width: 100%;\">\n\n <!-- Date Column -->\n <ng-container matColumnDef=\"date\">\n <th mat-header-cell *matHeaderCellDef style=\"width: 180px;\">Date</th>\n <td mat-cell *matCellDef=\"let notification\">\n {{ formatTimestamp(notification.created) | date }}\n </td>\n </ng-container>\n\n <!-- User Column -->\n <ng-container matColumnDef=\"user\">\n <th mat-header-cell *matHeaderCellDef style=\"width: 150px;\">User</th>\n <td mat-cell *matCellDef=\"let notification\">\n <strong>{{ notification.user_name }}</strong>\n </td>\n </ng-container>\n\n <!-- Message Column -->\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef>Message</th>\n <td mat-cell *matCellDef=\"let notification\">\n <div class=\"pill\" [style.background-color]=\"notification.content.sessionId.color || '#000000'\">\n {{ notification.content?.message || notification.content }}\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n } @else {\n <div style=\"text-align: center; color: #666; padding: 2rem;\">\n <mat-icon style=\"font-size: 48px; height: 48px; width: 48px; opacity: 0.5;\">notifications_none</mat-icon>\n <p>No notifications yet. Waiting for notifications on this channel...</p>\n </div>\n }\n </div>\n\n <!-- Disconnect Button - Below the table -->\n <div style=\"display: flex; justify-content: right; padding: 1rem;\">\n <button mat-raised-button color=\"warn\" (click)=\"onDisconnectFromChannel()\">\n Disconnect from Channel\n </button>\n </div>\n }\n }\n }\n\n</div>\n", styles: [".colored-message{font-weight:500;padding:4px 8px;border-radius:4px;display:inline-block;max-width:100%;word-wrap:break-word}.colored-message.black-text{color:#000!important}.colored-message.white-text{color:#fff!important;text-shadow:0 0 2px rgba(0,0,0,.5)}td.mat-mdc-cell{padding:8px 12px}.pill{border-radius:1rem;padding:.5rem 1rem;color:#fff}\n"], dependencies: [{ kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { 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: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i9.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i9.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i9.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i9.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i9.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i9.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i9.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i9.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i9.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i13.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i9$1.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i9$1.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i9$1.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] }); }
|
|
8103
8590
|
}
|
|
8104
8591
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsNotificationsComponent, decorators: [{
|
|
8105
8592
|
type: Component,
|
|
8106
|
-
args: [{ selector: 'app-ws-notifications', standalone: false, template: "<div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e8f5e9; border-radius: 4px;\">\n <strong>Create Notification Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input\n matInput\n [formControl]=\"newChannelName\"\n placeholder=\"Enter notification channel name\"\n (keydown.enter)=\"onCreateChannel()\"\n >\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; gap: 0.5rem;\">\n <button mat-raised-button color=\"accent\" (click)=\"onDefinePreviousChannels()\">\n Define Previous Channels\n </button
|
|
8593
|
+
args: [{ selector: 'app-ws-notifications', standalone: false, template: "<div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e8f5e9; border-radius: 4px;\">\n <strong>Create Notification Channel</strong>\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Channel Name</mat-label>\n <input\n matInput\n [formControl]=\"newChannelName\"\n placeholder=\"Enter notification channel name\"\n (keydown.enter)=\"onCreateChannel()\"\n >\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; gap: 0.5rem;\">\n <!-- <button mat-raised-button color=\"accent\" (click)=\"onDefinePreviousChannels()\">\n Define Previous Channels\n </button> -->\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel 1\n </button>\n </div>\n </div>\n </div>\n\n <!-- Channel Connection Section - Only shown when NOT connected -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (!isChannelConnected(subscribedChannels)) {\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Connect to Channel</strong>\n <div style=\"display: flex; gap: 1rem; margin-top: 0.5rem; flex-wrap: wrap; align-items: flex-start;\">\n <!-- Channel Selection (from today's DB data) -->\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\">\n <mat-label>Select Channel</mat-label>\n <mat-select [formControl]=\"selectedConnectionChannel\" placeholder=\"Select a notification channel\">\n @for (channel of (todaysNotificationChannels$ | async) || []; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n @empty {\n <mat-option disabled>No channels with data for today</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Date Filter -->\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\" [formGroup]=\"dateFilter\">\n <mat-label>Start Date</mat-label>\n <input matInput [matDatepicker]=\"startPicker\" formControlName=\"startDate\">\n <mat-datepicker-toggle matIconSuffix [for]=\"startPicker\"></mat-datepicker-toggle>\n <mat-datepicker #startPicker></mat-datepicker>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" style=\"flex: 1; min-width: 200px;\" [formGroup]=\"dateFilter\">\n <mat-label>End Date</mat-label>\n <input matInput [matDatepicker]=\"endPicker\" formControlName=\"endDate\">\n <mat-datepicker-toggle matIconSuffix [for]=\"endPicker\"></mat-datepicker-toggle>\n <mat-datepicker #endPicker></mat-datepicker>\n </mat-form-field>\n </div>\n\n <div style=\"display: flex; gap: 0.5rem;\">\n <span style=\"flex:1\"></span>\n <button\n mat-raised-button color=\"primary\"\n (click)=\"onConnectToChannel()\"\n [disabled]=\"!selectedConnectionChannel.value\"\n >\n Connect\n </button>\n </div>\n </div>\n }\n }\n\n <!-- Send Notification Section -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div\n style=\"padding: 1rem; background: #fff8e1; border-radius: 4px;\"\n [formGroup]=\"notificationForm\"\n >\n <strong>Send Notification</strong>\n\n <!-- Notification Content -->\n <div style=\"display: flex; flex-direction: column; margin-top: 0.5rem;\">\n <mat-form-field style=\"width: 100%;\" appearance=\"outline\">\n <mat-label>Notification Message</mat-label>\n <textarea\n matInput placeholder=\"Type your notification...\"\n formControlName=\"content\"\n rows=\"2\"\n (keydown.enter)=\"onSendNotification(); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n <div style=\"display: flex; justify-content: flex-end; margin-top: -0.5rem;\">\n <button\n mat-raised-button color=\"accent\"\n (click)=\"onSendNotification()\"\n [disabled]=\"notificationForm.invalid\"\n >\n Send Notification\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n <!-- Notifications Table - Only shown when connected -->\n @if ((subscribedNotificationChannels$ | async); as subscribedChannels) {\n @if (isChannelConnected(subscribedChannels)) {\n @if ((notificationMessages$ | async); as notifications) {\n <div>\n <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n <strong>Notifications ({{ notifications.length }})</strong>\n </div>\n\n @if (notifications.length > 0) {\n <div style=\"margin-top: 0.5rem; height: 400px; overflow: auto; border: 1px solid #888; border-radius: 4px;\">\n <table mat-table [dataSource]=\"notifications\" style=\"width: 100%;\">\n\n <!-- Date Column -->\n <ng-container matColumnDef=\"date\">\n <th mat-header-cell *matHeaderCellDef style=\"width: 180px;\">Date</th>\n <td mat-cell *matCellDef=\"let notification\">\n {{ formatTimestamp(notification.created) | date }}\n </td>\n </ng-container>\n\n <!-- User Column -->\n <ng-container matColumnDef=\"user\">\n <th mat-header-cell *matHeaderCellDef style=\"width: 150px;\">User</th>\n <td mat-cell *matCellDef=\"let notification\">\n <strong>{{ notification.user_name }}</strong>\n </td>\n </ng-container>\n\n <!-- Message Column -->\n <ng-container matColumnDef=\"message\">\n <th mat-header-cell *matHeaderCellDef>Message</th>\n <td mat-cell *matCellDef=\"let notification\">\n <div class=\"pill\" [style.background-color]=\"notification.content.sessionId.color || '#000000'\">\n {{ notification.content?.message || notification.content }}\n </div>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns; sticky: true\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n } @else {\n <div style=\"text-align: center; color: #666; padding: 2rem;\">\n <mat-icon style=\"font-size: 48px; height: 48px; width: 48px; opacity: 0.5;\">notifications_none</mat-icon>\n <p>No notifications yet. Waiting for notifications on this channel...</p>\n </div>\n }\n </div>\n\n <!-- Disconnect Button - Below the table -->\n <div style=\"display: flex; justify-content: right; padding: 1rem;\">\n <button mat-raised-button color=\"warn\" (click)=\"onDisconnectFromChannel()\">\n Disconnect from Channel\n </button>\n </div>\n }\n }\n }\n\n</div>\n", styles: [".colored-message{font-weight:500;padding:4px 8px;border-radius:4px;display:inline-block;max-width:100%;word-wrap:break-word}.colored-message.black-text{color:#000!important}.colored-message.white-text{color:#fff!important;text-shadow:0 0 2px rgba(0,0,0,.5)}td.mat-mdc-cell{padding:8px 12px}.pill{border-radius:1rem;padding:.5rem 1rem;color:#fff}\n"] }]
|
|
8107
8594
|
}], propDecorators: { server: [{
|
|
8108
8595
|
type: Input
|
|
8109
8596
|
}], wsServer: [{
|
|
@@ -8399,8 +8886,9 @@ class HttpRequestServicesDemoComponent {
|
|
|
8399
8886
|
{ name: "Local Storage Service", value: 'local_storage_service' },
|
|
8400
8887
|
// { name: "Local Signals Storage Service", value: 'local_storage_signals_service', new: true },
|
|
8401
8888
|
{ name: "Store State Manager Service", value: 'store_state_manager', new: true },
|
|
8889
|
+
{ name: "Basic Http Service", value: 'basic_http_service', divider: true },
|
|
8402
8890
|
];
|
|
8403
|
-
this.selectedService = this.requestTypes[
|
|
8891
|
+
this.selectedService = this.requestTypes[1].value; //menu selection default
|
|
8404
8892
|
}
|
|
8405
8893
|
ngOnInit() {
|
|
8406
8894
|
if (this.configOptions)
|
|
@@ -8410,11 +8898,11 @@ class HttpRequestServicesDemoComponent {
|
|
|
8410
8898
|
this.selectedService = this.requestTypes[type].value;
|
|
8411
8899
|
}
|
|
8412
8900
|
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 }); }
|
|
8413
|
-
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 ('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: 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" }] }); }
|
|
8901
|
+
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" }] }); }
|
|
8414
8902
|
}
|
|
8415
8903
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestServicesDemoComponent, decorators: [{
|
|
8416
8904
|
type: Component,
|
|
8417
|
-
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 ('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"] }]
|
|
8905
|
+
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"] }]
|
|
8418
8906
|
}], ctorParameters: () => [{ type: ConfigOptions, decorators: [{
|
|
8419
8907
|
type: Inject,
|
|
8420
8908
|
args: [CONFIG_SETTINGS_TOKEN]
|
|
@@ -8960,7 +9448,8 @@ class HttpRequestManagerModule {
|
|
|
8960
9448
|
};
|
|
8961
9449
|
}
|
|
8962
9450
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestManagerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
8963
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestManagerModule, declarations: [
|
|
9451
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: HttpRequestManagerModule, declarations: [RequestManagerBasicDemoComponent,
|
|
9452
|
+
HttpRequestServicesDemoComponent,
|
|
8964
9453
|
RequestManagerStateDemoComponent,
|
|
8965
9454
|
RequestManagerDemoComponent,
|
|
8966
9455
|
RequestSignalsManagerDemoComponent,
|
|
@@ -9062,6 +9551,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
9062
9551
|
// StoreStateManagerModule
|
|
9063
9552
|
],
|
|
9064
9553
|
declarations: [
|
|
9554
|
+
RequestManagerBasicDemoComponent,
|
|
9065
9555
|
HttpRequestServicesDemoComponent,
|
|
9066
9556
|
RequestManagerStateDemoComponent,
|
|
9067
9557
|
RequestManagerDemoComponent,
|