@c8y/tutorial 1023.0.2 → 1023.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -885,6 +885,13 @@ export default {
885
885
  description: 'This is an example for the bottom drawer service.',
886
886
  scope: 'self'
887
887
  },
888
+ {
889
+ name: 'AI Components',
890
+ module: 'aiComponentsProviders',
891
+ path: './src/ai/ai-components.providers.ts',
892
+ description: 'Showcasing the most common AI components.',
893
+ scope: 'self'
894
+ },
888
895
  {
889
896
  name: 'Computed asset properties',
890
897
  module: 'computedAssetPropertiesProviders',
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@c8y/tutorial",
3
- "version": "1023.0.2",
3
+ "version": "1023.4.5",
4
4
  "description": "This package is used to scaffold a tutorial for Cumulocity IoT Web SDK which explains a lot of concepts.",
5
5
  "dependencies": {
6
- "@c8y/style": "1023.0.2",
7
- "@c8y/ngx-components": "1023.0.2",
8
- "@c8y/client": "1023.0.2",
9
- "@c8y/bootstrap": "1023.0.2",
6
+ "@c8y/style": "1023.4.5",
7
+ "@c8y/ngx-components": "1023.4.5",
8
+ "@c8y/client": "1023.4.5",
9
+ "@c8y/bootstrap": "1023.4.5",
10
10
  "@angular/cdk": "^20.2.7",
11
11
  "monaco-editor": "~0.53.0",
12
12
  "ngx-bootstrap": "20.0.2",
@@ -14,8 +14,8 @@
14
14
  "rxjs": "7.8.2"
15
15
  },
16
16
  "devDependencies": {
17
- "@c8y/options": "1023.0.2",
18
- "@c8y/devkit": "1023.0.2"
17
+ "@c8y/options": "1023.4.5",
18
+ "@c8y/devkit": "1023.4.5"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "@angular/common": ">=20 <21"
@@ -0,0 +1,249 @@
1
+ <c8y-title>Chat component example</c8y-title>
2
+ <div class="row d-flex-sm">
3
+ <div class="col-md-4 p-0">
4
+ <div class="d-col p-16 form-group-sm bg-level-1">
5
+ <p class="text-medium p-b-16">Config options</p>
6
+
7
+ <label>title</label>
8
+ <div class="input-group input-group-sm m-b-8">
9
+ <input
10
+ class="form-control"
11
+ type="text"
12
+ [(ngModel)]="config.title"
13
+ />
14
+ <div class="input-group-btn">
15
+ <button
16
+ class="btn btn-default"
17
+ (click)="reloadComponent()"
18
+ >
19
+ {{ 'Apply' | translate }}
20
+ </button>
21
+ </div>
22
+ </div>
23
+
24
+ <label>headline</label>
25
+ <div class="input-group input-group-sm m-b-8">
26
+ <input
27
+ class="form-control"
28
+ type="text"
29
+ [(ngModel)]="config.headline"
30
+ />
31
+ <div class="input-group-btn">
32
+ <button
33
+ class="btn btn-default"
34
+ (click)="reloadComponent()"
35
+ >
36
+ {{ 'Apply' | translate }}
37
+ </button>
38
+ </div>
39
+ </div>
40
+
41
+ <label>welcomeText</label>
42
+ <div class="input-group input-group-sm m-b-8">
43
+ <input
44
+ class="form-control"
45
+ type="text"
46
+ [(ngModel)]="config.welcomeText"
47
+ />
48
+ <div class="input-group-btn">
49
+ <button
50
+ class="btn btn-default"
51
+ (click)="reloadComponent()"
52
+ >
53
+ {{ 'Apply' | translate }}
54
+ </button>
55
+ </div>
56
+ </div>
57
+
58
+ <label>placeholder</label>
59
+ <div class="input-group input-group-sm m-b-8">
60
+ <input
61
+ class="form-control"
62
+ type="text"
63
+ [(ngModel)]="config.placeholder"
64
+ />
65
+ <div class="input-group-btn">
66
+ <button
67
+ class="btn btn-default"
68
+ (click)="reloadComponent()"
69
+ >
70
+ {{ 'Apply' | translate }}
71
+ </button>
72
+ </div>
73
+ </div>
74
+
75
+ <label>sendButtonText (displayed as a tooltip)</label>
76
+ <div class="input-group input-group-sm m-b-8">
77
+ <input
78
+ class="form-control"
79
+ type="text"
80
+ [(ngModel)]="config.sendButtonText"
81
+ />
82
+ <div class="input-group-btn">
83
+ <button
84
+ class="btn btn-default"
85
+ (click)="reloadComponent()"
86
+ >
87
+ {{ 'Apply' | translate }}
88
+ </button>
89
+ </div>
90
+ </div>
91
+
92
+ <label>cancelButtonText (displayed as a tooltip)</label>
93
+ <div class="input-group input-group-sm m-b-8">
94
+ <input
95
+ class="form-control"
96
+ type="text"
97
+ [(ngModel)]="config.cancelButtonText"
98
+ />
99
+ <div class="input-group-btn">
100
+ <button
101
+ class="btn btn-default"
102
+ (click)="reloadComponent()"
103
+ >
104
+ {{ 'Apply' | translate }}
105
+ </button>
106
+ </div>
107
+ </div>
108
+
109
+ <label>disclaimerText</label>
110
+ <div class="input-group input-group-sm m-b-8">
111
+ <input
112
+ class="form-control"
113
+ type="text"
114
+ [(ngModel)]="config.disclaimerText"
115
+ />
116
+ <div class="input-group-btn">
117
+ <button
118
+ class="btn btn-default"
119
+ (click)="reloadComponent()"
120
+ >
121
+ {{ 'Apply' | translate }}
122
+ </button>
123
+ </div>
124
+ </div>
125
+
126
+ <pre class="m-t-16 inner-scroll small">{{ config | json }}</pre>
127
+ </div>
128
+ </div>
129
+ <div class="col-md-8">
130
+ @if (showComponent) {
131
+ <label
132
+ class="c8y-switch m-t-8"
133
+ title="showInitialMessage"
134
+ >
135
+ <input
136
+ class="form-control"
137
+ type="checkbox"
138
+ [(ngModel)]="showInitialMessage"
139
+ (ngModelChange)="reloadComponent()"
140
+ />
141
+ <span></span>
142
+ <span>Show example message with content projection</span>
143
+ </label>
144
+ <div
145
+ class="card m-t-16"
146
+ style="height: 530px"
147
+ >
148
+ <c8y-ai-chat
149
+ [config]="config"
150
+ (onMessage)="sendMessage($event)"
151
+ [isLoading]="isLoading"
152
+ >
153
+ @if (showInitialMessage) {
154
+ <c8y-ai-chat-message>
155
+ You can also use content projection to add any dynamic content to the chat bubble.
156
+ <div class="d-flex justify-content-end">
157
+ <button class="btn btn-default btn-sm m-t-8">Get device details</button>
158
+ <button class="btn btn-default btn-sm m-t-8 m-l-4">Get all measurements</button>
159
+ </div>
160
+ <c8y-ai-chat-message-action
161
+ icon="rocket"
162
+ tooltip="Get device details"
163
+ ></c8y-ai-chat-message-action>
164
+ </c8y-ai-chat-message>
165
+ }
166
+ @for (message of displayMessages; track message) {
167
+ <c8y-ai-chat-message [message]="message">
168
+ @if (message.role === 'assistant') {
169
+ <c8y-ai-chat-message-action
170
+ icon="thumbs-up"
171
+ tooltip="This is useful"
172
+ ></c8y-ai-chat-message-action>
173
+ }
174
+
175
+ @if (message.role === 'assistant') {
176
+ <c8y-ai-chat-message-action
177
+ icon="thumbs-down"
178
+ tooltip="This is not useful"
179
+ ></c8y-ai-chat-message-action>
180
+ }
181
+
182
+ @if (message.role === 'assistant') {
183
+ <c8y-ai-chat-message-action
184
+ icon="undo"
185
+ tooltip="Revert"
186
+ (click)="revertMessage(message)"
187
+ ></c8y-ai-chat-message-action>
188
+ }
189
+
190
+ @if (message.role === 'user') {
191
+ <c8y-ai-chat-message-action
192
+ icon="edit1"
193
+ tooltip="Edit"
194
+ ></c8y-ai-chat-message-action>
195
+ }
196
+
197
+ @if (message.role === 'user') {
198
+ <c8y-ai-chat-message-action
199
+ icon="clone"
200
+ tooltip="Copy"
201
+ ></c8y-ai-chat-message-action>
202
+ }
203
+ </c8y-ai-chat-message>
204
+ }
205
+ @if (isLoading) {
206
+ <c8y-ai-chat-message>
207
+ <c8y-loading [message]="'Loading...'"></c8y-loading>
208
+ </c8y-ai-chat-message>
209
+ }
210
+
211
+ <c8y-ai-chat-suggestion
212
+ [label]="'Create device '"
213
+ [prompt]="'AHOI, create a new device'"
214
+ (suggestionClicked)="sendMessage($event)"
215
+ ></c8y-ai-chat-suggestion>
216
+
217
+ <c8y-ai-chat-suggestion
218
+ [icon]="'forward'"
219
+ [label]="'Mix and match'"
220
+ [useAiButtons]="true"
221
+ [prompt]="'Create a device with a measurement and an event'"
222
+ (suggestionClicked)="sendMessage($event)"
223
+ ></c8y-ai-chat-suggestion>
224
+ </c8y-ai-chat>
225
+ </div>
226
+ }
227
+
228
+ <div class="p-16 bg-level-1">
229
+ <p class="text-medium">Show timestamps on chat messages</p>
230
+ <p class="text-muted text-12 m-b-16">
231
+ The option controls the AIMessage objects passed to the component, it's not part of the
232
+ configuration.
233
+ </p>
234
+ <label
235
+ class="c8y-checkbox m-t-8"
236
+ title="showTimestamps"
237
+ >
238
+ <input
239
+ class="form-control"
240
+ type="checkbox"
241
+ [(ngModel)]="showTimestamps"
242
+ (ngModelChange)="reloadComponent()"
243
+ />
244
+ <span></span>
245
+ <span>showTimestamps</span>
246
+ </label>
247
+ </div>
248
+ </div>
249
+ </div>
@@ -0,0 +1,92 @@
1
+ import { JsonPipe } from '@angular/common';
2
+ import { Component } from '@angular/core';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { CoreModule, LoadingComponent } from '@c8y/ngx-components';
5
+ import { AIMessage } from '@c8y/ngx-components/ai';
6
+ import {
7
+ AiChatComponent,
8
+ AiChatMessageActionComponent,
9
+ AiChatMessageComponent,
10
+ AiChatSuggestionComponent
11
+ } from '@c8y/ngx-components/ai/ai-chat';
12
+
13
+ @Component({
14
+ selector: 'c8y-ai-chat-example',
15
+ templateUrl: './ai-chat-example.component.html',
16
+ standalone: true,
17
+ imports: [
18
+ AiChatComponent,
19
+ AiChatMessageComponent,
20
+ AiChatMessageActionComponent,
21
+ AiChatSuggestionComponent,
22
+ LoadingComponent,
23
+ CoreModule,
24
+ FormsModule,
25
+ JsonPipe
26
+ ]
27
+ })
28
+ export class ChatExampleComponent {
29
+ private _showTimestamps = false;
30
+ showInitialMessage = false;
31
+
32
+ get showTimestamps(): boolean {
33
+ return this._showTimestamps;
34
+ }
35
+
36
+ set showTimestamps(value: boolean) {
37
+ this._showTimestamps = value;
38
+ this.updateDisplayMessages();
39
+ }
40
+
41
+ config = {
42
+ headline: 'Welcome!',
43
+ welcomeText: 'Type a message below to get started.',
44
+ title: 'What can I help you with?',
45
+ placeholder: 'Type your message here...',
46
+ sendButtonText: 'Send',
47
+ cancelButtonText: 'Cancel',
48
+ disclaimerText: 'AI-generated responses can contain errors. Verify the details before use.',
49
+ userInterfaceIcons: { send: 'arrow-circle-up', cancel: 'stop-circle' }
50
+ };
51
+
52
+ messages: AIMessage[] = [];
53
+ displayMessages: AIMessage[] = [];
54
+ showComponent = true;
55
+
56
+ isLoading = false;
57
+
58
+ async sendMessage(message: AIMessage): Promise<void> {
59
+ this.isLoading = true;
60
+ this.messages.push(message);
61
+ this.updateDisplayMessages();
62
+ setTimeout(() => {
63
+ // Simulate a delay for the response
64
+ this.messages.push({
65
+ content: 'Thank you for your message!',
66
+ role: 'assistant',
67
+ timestamp: new Date().toISOString()
68
+ });
69
+ this.updateDisplayMessages();
70
+ this.isLoading = false;
71
+ }, 1000);
72
+ }
73
+
74
+ revertMessage(message: AIMessage) {
75
+ this.messages = this.messages.filter(m => m !== message);
76
+ this.updateDisplayMessages();
77
+ }
78
+
79
+ reloadComponent() {
80
+ // Reload the component by toggling the flag
81
+ this.showComponent = false;
82
+ setTimeout(() => {
83
+ this.showComponent = true;
84
+ });
85
+ }
86
+
87
+ private updateDisplayMessages() {
88
+ this.displayMessages = this._showTimestamps
89
+ ? this.messages
90
+ : this.messages.map(({ timestamp: _timestamp, ...msg }) => msg);
91
+ }
92
+ }
@@ -0,0 +1,17 @@
1
+ import { hookNavigator, hookRoute, NavigatorNode } from '@c8y/ngx-components';
2
+
3
+ export const aiComponentsProviders = [
4
+ hookRoute({
5
+ path: 'ai/chat',
6
+ loadComponent: () => import('./ai-chat-example.component').then(m => m.ChatExampleComponent)
7
+ }),
8
+ hookNavigator(
9
+ new NavigatorNode({
10
+ priority: 75,
11
+ path: 'ai/chat',
12
+ icon: 'chat',
13
+ label: 'AI Chat',
14
+ parent: 'AI'
15
+ })
16
+ )
17
+ ];