ciyidogan commited on
Commit
d08043e
·
verified ·
1 Parent(s): e40119d

Update flare-ui/src/app/components/chat/chat.component.ts

Browse files
flare-ui/src/app/components/chat/chat.component.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Component, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core';
2
  import { CommonModule } from '@angular/common';
3
  import { FormBuilder, ReactiveFormsModule, Validators, FormsModule } from '@angular/forms';
4
  import { MatButtonModule } from '@angular/material/button';
@@ -7,8 +7,9 @@ import { MatFormFieldModule } from '@angular/material/form-field';
7
  import { MatInputModule } from '@angular/material/input';
8
  import { MatCardModule } from '@angular/material/card';
9
  import { MatSelectModule } from '@angular/material/select';
10
- import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
11
  import { MatDividerModule } from '@angular/material/divider';
 
 
12
  import { Subscription } from 'rxjs';
13
 
14
  import { ApiService } from '../../services/api.service';
@@ -32,398 +33,152 @@ interface ChatMessage {
32
  MatInputModule,
33
  MatCardModule,
34
  MatSelectModule,
35
- MatProgressSpinnerModule,
36
- MatDividerModule
 
37
  ],
38
- template: `
39
- <div class="chat-container">
40
- <mat-card>
41
- <mat-card-header>
42
- <mat-icon mat-card-avatar>chat_bubble_outline</mat-icon>
43
- <mat-card-title>Chat Test</mat-card-title>
44
- <mat-card-subtitle>Test your project's chat functionality</mat-card-subtitle>
45
- </mat-card-header>
46
-
47
- <mat-card-content>
48
- <!-- Project Selection -->
49
- <div class="project-selector" *ngIf="!sessionId">
50
- <mat-form-field appearance="outline" class="full-width">
51
- <mat-label>Select Project</mat-label>
52
- <mat-select [(ngModel)]="selectedProject" [ngModelOptions]="{standalone: true}">
53
- <mat-option *ngFor="let project of projects" [value]="project">
54
- {{ project }}
55
- </mat-option>
56
- </mat-select>
57
- <mat-hint>Choose a project to start chatting</mat-hint>
58
- </mat-form-field>
59
-
60
- <button mat-raised-button
61
- color="primary"
62
- (click)="startChat()"
63
- [disabled]="!selectedProject || loading"
64
- class="start-button">
65
- <mat-icon>play_arrow</mat-icon>
66
- Start Chat
67
- </button>
68
- </div>
69
-
70
- <!-- Chat Interface -->
71
- <div class="chat-interface" *ngIf="sessionId">
72
- <div class="chat-header">
73
- <span class="project-name">{{ selectedProject }}</span>
74
- <button mat-icon-button (click)="endChat()" matTooltip="End Chat">
75
- <mat-icon>close</mat-icon>
76
- </button>
77
- </div>
78
-
79
- <mat-divider></mat-divider>
80
-
81
- <div class="messages-container" #messagesContainer>
82
- <div *ngFor="let message of messages"
83
- class="message"
84
- [class.user-message]="message.author === 'user'"
85
- [class.assistant-message]="message.author === 'assistant'">
86
- <div class="message-bubble">
87
- <mat-icon class="author-icon">
88
- {{ message.author === 'user' ? 'person' : 'smart_toy' }}
89
- </mat-icon>
90
- <div class="message-content">
91
- <div class="message-text">{{ message.text }}</div>
92
- <div class="message-time" *ngIf="message.timestamp">
93
- {{ message.timestamp | date:'short' }}
94
- </div>
95
- </div>
96
- </div>
97
- </div>
98
-
99
- <div class="typing-indicator" *ngIf="loading">
100
- <mat-spinner diameter="20"></mat-spinner>
101
- <span>Assistant is typing...</span>
102
- </div>
103
- </div>
104
-
105
- <mat-divider></mat-divider>
106
-
107
- <div class="input-container">
108
- <form [formGroup]="messageForm" (ngSubmit)="sendMessage()" class="message-form">
109
- <mat-form-field appearance="outline" class="message-input">
110
- <mat-label>Type your message</mat-label>
111
- <textarea matInput
112
- formControlName="message"
113
- rows="2"
114
- (keydown.enter)="$event.shiftKey || (sendMessage(), $event.preventDefault())"
115
- placeholder="Press Enter to send, Shift+Enter for new line">
116
- </textarea>
117
- <mat-hint>Session ID: {{ sessionId }}</mat-hint>
118
- </mat-form-field>
119
-
120
- <button mat-fab
121
- color="primary"
122
- type="submit"
123
- [disabled]="!messageForm.valid || loading"
124
- class="send-button">
125
- <mat-icon>send</mat-icon>
126
- </button>
127
- </form>
128
- </div>
129
- </div>
130
-
131
- <!-- Error Display -->
132
- <div class="error-message" *ngIf="error">
133
- <mat-icon>error</mat-icon>
134
- <span>{{ error }}</span>
135
- </div>
136
- </mat-card-content>
137
- </mat-card>
138
- </div>
139
- `,
140
- styles: [`
141
- .chat-container {
142
- max-width: 800px;
143
- margin: 0 auto;
144
- padding: 20px;
145
- }
146
-
147
- .full-width {
148
- width: 100%;
149
- }
150
-
151
- .project-selector {
152
- display: flex;
153
- gap: 16px;
154
- align-items: flex-start;
155
- margin-bottom: 20px;
156
- }
157
-
158
- .project-selector mat-form-field {
159
- flex: 1;
160
- }
161
-
162
- .start-button {
163
- margin-top: 8px;
164
- }
165
-
166
- .chat-interface {
167
- display: flex;
168
- flex-direction: column;
169
- height: 600px;
170
- }
171
-
172
- .chat-header {
173
- display: flex;
174
- justify-content: space-between;
175
- align-items: center;
176
- padding: 12px 0;
177
- }
178
-
179
- .project-name {
180
- font-weight: 500;
181
- font-size: 16px;
182
- }
183
-
184
- .messages-container {
185
- flex: 1;
186
- overflow-y: auto;
187
- padding: 20px 0;
188
- background-color: #f5f5f5;
189
- border-radius: 8px;
190
- margin: 16px 0;
191
- }
192
-
193
- .message {
194
- margin-bottom: 16px;
195
- padding: 0 16px;
196
- }
197
-
198
- .message-bubble {
199
- display: flex;
200
- gap: 12px;
201
- max-width: 70%;
202
- }
203
-
204
- .user-message .message-bubble {
205
- margin-left: auto;
206
- flex-direction: row-reverse;
207
- }
208
-
209
- .author-icon {
210
- width: 32px;
211
- height: 32px;
212
- display: flex;
213
- align-items: center;
214
- justify-content: center;
215
- border-radius: 50%;
216
- flex-shrink: 0;
217
- }
218
-
219
- .user-message .author-icon {
220
- background-color: #3f51b5;
221
- color: white;
222
- }
223
-
224
- .assistant-message .author-icon {
225
- background-color: #e0e0e0;
226
- color: #666;
227
- }
228
-
229
- .message-content {
230
- background-color: white;
231
- border-radius: 12px;
232
- padding: 12px 16px;
233
- box-shadow: 0 1px 2px rgba(0,0,0,0.1);
234
- }
235
-
236
- .user-message .message-content {
237
- background-color: #3f51b5;
238
- color: white;
239
- }
240
-
241
- .message-text {
242
- white-space: pre-wrap;
243
- word-break: break-word;
244
- }
245
-
246
- .message-time {
247
- font-size: 11px;
248
- opacity: 0.7;
249
- margin-top: 4px;
250
- }
251
-
252
- .typing-indicator {
253
- display: flex;
254
- align-items: center;
255
- gap: 8px;
256
- padding: 0 16px;
257
- color: #666;
258
- }
259
-
260
- .input-container {
261
- padding-top: 16px;
262
- }
263
-
264
- .message-form {
265
- display: flex;
266
- gap: 12px;
267
- align-items: flex-start;
268
- }
269
-
270
- .message-input {
271
- flex: 1;
272
- }
273
-
274
- .send-button {
275
- margin-top: 8px;
276
- }
277
-
278
- .error-message {
279
- display: flex;
280
- align-items: center;
281
- gap: 8px;
282
- color: #f44336;
283
- margin-top: 16px;
284
- padding: 12px;
285
- background-color: #ffebee;
286
- border-radius: 4px;
287
- }
288
-
289
- ::ng-deep {
290
- .mat-mdc-form-field-subscript-wrapper {
291
- margin-top: 0.2em;
292
- }
293
-
294
- .mat-mdc-text-field-wrapper {
295
- background-color: white;
296
- }
297
- }
298
- `]
299
  })
300
- export class ChatComponent implements OnInit, OnDestroy {
301
- @ViewChild('messagesContainer') private messagesContainer!: ElementRef;
302
 
303
  projects: string[] = [];
304
- selectedProject: string | null = null;
 
305
  sessionId: string | null = null;
306
  messages: ChatMessage[] = [];
 
 
307
  loading = false;
308
  error = '';
309
 
310
- messageForm = this.fb.group({
311
- message: ['', Validators.required]
312
- });
313
-
314
  private subs = new Subscription();
 
315
 
316
  constructor(
317
- private fb: FormBuilder,
318
- private apiService: ApiService
319
  ) {}
320
 
321
- ngOnInit() {
322
  this.loadProjects();
323
  }
324
 
325
- ngOnDestroy() {
 
 
 
 
 
 
 
326
  this.subs.unsubscribe();
327
  }
328
 
329
- loadProjects() {
330
  this.loading = true;
331
- this.subs.add(
332
- this.apiService.getChatProjects().subscribe({
333
- next: (projects) => {
334
- this.projects = projects;
335
- this.loading = false;
336
- },
337
- error: (err) => {
338
- this.error = 'Failed to load projects';
339
- this.loading = false;
340
  }
341
- })
342
- );
 
 
 
 
 
 
343
  }
344
 
345
- startChat() {
346
  if (!this.selectedProject) return;
347
 
348
  this.loading = true;
349
  this.error = '';
350
 
351
- this.subs.add(
352
- this.apiService.startChat(this.selectedProject).subscribe({
353
- next: (response) => {
354
- this.sessionId = response.session_id;
355
- this.messages = [];
356
-
357
- if (response.answer) {
358
- this.messages.push({
359
- author: 'assistant',
360
- text: response.answer,
361
- timestamp: new Date()
362
- });
363
- }
364
-
365
- this.loading = false;
366
- this.messageForm.reset();
367
- this.scrollToBottom();
368
- },
369
- error: (err) => {
370
- this.error = err.error?.detail || 'Failed to start chat';
371
- this.loading = false;
372
- }
373
- })
374
- );
375
  }
376
 
377
- sendMessage() {
378
- if (!this.messageForm.valid || !this.sessionId || this.loading) return;
379
-
380
- const messageText = this.messageForm.value.message!.trim();
381
- if (!messageText) return;
382
 
 
 
 
383
  // Add user message
384
- this.messages.push({
385
- author: 'user',
386
- text: messageText,
387
  timestamp: new Date()
388
  });
389
 
 
390
  this.loading = true;
391
- this.messageForm.reset();
392
- this.scrollToBottom();
393
-
394
- this.subs.add(
395
- this.apiService.chat(this.sessionId, messageText).subscribe({
396
- next: (response) => {
397
- this.messages.push({
398
- author: 'assistant',
399
- text: response.answer,
400
- timestamp: new Date()
401
- });
402
-
403
- this.loading = false;
404
- this.scrollToBottom();
405
- },
406
- error: (err) => {
407
- this.error = err.error?.detail || 'Failed to send message';
408
- this.loading = false;
409
- }
410
- })
411
- );
 
 
 
 
412
  }
413
 
414
- endChat() {
415
  this.sessionId = null;
416
  this.messages = [];
417
- this.messageForm.reset();
 
418
  this.error = '';
419
  }
420
 
421
  private scrollToBottom(): void {
422
- setTimeout(() => {
423
- if (this.messagesContainer) {
424
- const element = this.messagesContainer.nativeElement;
425
- element.scrollTop = element.scrollHeight;
426
  }
427
- }, 100);
 
 
428
  }
429
  }
 
1
+ import { Component, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewChecked } from '@angular/core';
2
  import { CommonModule } from '@angular/common';
3
  import { FormBuilder, ReactiveFormsModule, Validators, FormsModule } from '@angular/forms';
4
  import { MatButtonModule } from '@angular/material/button';
 
7
  import { MatInputModule } from '@angular/material/input';
8
  import { MatCardModule } from '@angular/material/card';
9
  import { MatSelectModule } from '@angular/material/select';
 
10
  import { MatDividerModule } from '@angular/material/divider';
11
+ import { MatTooltipModule } from '@angular/material/tooltip';
12
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
13
  import { Subscription } from 'rxjs';
14
 
15
  import { ApiService } from '../../services/api.service';
 
33
  MatInputModule,
34
  MatCardModule,
35
  MatSelectModule,
36
+ MatDividerModule,
37
+ MatTooltipModule,
38
+ MatProgressSpinnerModule
39
  ],
40
+ templateUrl: './chat.component.html',
41
+ styleUrls: ['./chat.component.scss']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  })
43
+ export class ChatComponent implements OnInit, OnDestroy, AfterViewChecked {
44
+ @ViewChild('scrollMe') private myScrollContainer!: ElementRef;
45
 
46
  projects: string[] = [];
47
+ selectedProject: string | null = null; // Düzeltildi: ! kaldırıldı, null değer atandı
48
+
49
  sessionId: string | null = null;
50
  messages: ChatMessage[] = [];
51
+ input = this.fb.control('', Validators.required);
52
+
53
  loading = false;
54
  error = '';
55
 
 
 
 
 
56
  private subs = new Subscription();
57
+ private shouldScroll = false;
58
 
59
  constructor(
60
+ private fb: FormBuilder,
61
+ private api: ApiService
62
  ) {}
63
 
64
+ ngOnInit(): void {
65
  this.loadProjects();
66
  }
67
 
68
+ ngAfterViewChecked() {
69
+ if (this.shouldScroll) {
70
+ this.scrollToBottom();
71
+ this.shouldScroll = false;
72
+ }
73
+ }
74
+
75
+ ngOnDestroy(): void {
76
  this.subs.unsubscribe();
77
  }
78
 
79
+ loadProjects(): void {
80
  this.loading = true;
81
+ const sub = this.api.getChatProjects().subscribe({
82
+ next: projects => {
83
+ this.projects = projects;
84
+ this.loading = false;
85
+ if (projects.length === 0) {
86
+ this.error = 'No enabled projects found. Please enable a project with published version.';
 
 
 
87
  }
88
+ },
89
+ error: (err) => {
90
+ this.error = 'Failed to load projects';
91
+ this.loading = false;
92
+ console.error('Load projects error:', err);
93
+ }
94
+ });
95
+ this.subs.add(sub);
96
  }
97
 
98
+ startChat(): void {
99
  if (!this.selectedProject) return;
100
 
101
  this.loading = true;
102
  this.error = '';
103
 
104
+ const sub = this.api.startChat(this.selectedProject).subscribe({
105
+ next: res => {
106
+ this.sessionId = res.session_id;
107
+ this.messages = [{
108
+ author: 'assistant',
109
+ text: res.answer,
110
+ timestamp: new Date()
111
+ }];
112
+ this.loading = false;
113
+ this.shouldScroll = true;
114
+ },
115
+ error: (err) => {
116
+ this.error = err.error?.detail || 'Failed to start session';
117
+ this.loading = false;
118
+ console.error('Start chat error:', err);
119
+ }
120
+ });
121
+ this.subs.add(sub);
 
 
 
 
 
 
122
  }
123
 
124
+ send(): void {
125
+ if (!this.sessionId || this.input.invalid) return;
 
 
 
126
 
127
+ const text = this.input.value!.trim();
128
+ if (!text) return;
129
+
130
  // Add user message
131
+ this.messages.push({
132
+ author: 'user',
133
+ text,
134
  timestamp: new Date()
135
  });
136
 
137
+ this.input.reset();
138
  this.loading = true;
139
+ this.shouldScroll = true;
140
+
141
+ // Send to backend
142
+ const sub = this.api.chat(this.sessionId, text).subscribe({
143
+ next: res => {
144
+ this.messages.push({
145
+ author: 'assistant',
146
+ text: res.answer,
147
+ timestamp: new Date()
148
+ });
149
+ this.loading = false;
150
+ this.shouldScroll = true;
151
+ },
152
+ error: (err) => {
153
+ this.messages.push({
154
+ author: 'assistant',
155
+ text: '⚠️ ' + (err.error?.detail || 'Failed to send message. Please try again.'),
156
+ timestamp: new Date()
157
+ });
158
+ this.loading = false;
159
+ this.shouldScroll = true;
160
+ console.error('Chat error:', err);
161
+ }
162
+ });
163
+ this.subs.add(sub);
164
  }
165
 
166
+ endSession(): void {
167
  this.sessionId = null;
168
  this.messages = [];
169
+ this.selectedProject = null;
170
+ this.input.reset();
171
  this.error = '';
172
  }
173
 
174
  private scrollToBottom(): void {
175
+ try {
176
+ if (this.myScrollContainer) {
177
+ this.myScrollContainer.nativeElement.scrollTop =
178
+ this.myScrollContainer.nativeElement.scrollHeight;
179
  }
180
+ } catch(err) {
181
+ console.error('Scroll error:', err);
182
+ }
183
  }
184
  }