ciyidogan commited on
Commit
0f5e454
·
verified ·
1 Parent(s): 2c3dbda

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 } 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,6 +7,8 @@ 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 { Subscription } from 'rxjs';
11
 
12
  import { ApiService } from '../../services/api.service';
@@ -14,6 +16,7 @@ import { ApiService } from '../../services/api.service';
14
  interface ChatMessage {
15
  author: 'user' | 'assistant';
16
  text: string;
 
17
  }
18
 
19
  @Component({
@@ -28,65 +31,399 @@ interface ChatMessage {
28
  MatFormFieldModule,
29
  MatInputModule,
30
  MatCardModule,
31
- MatSelectModule
 
 
32
  ],
33
- templateUrl: './chat.component.html',
34
- styleUrls: ['./chat.component.scss']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  })
36
  export class ChatComponent implements OnInit, OnDestroy {
37
- projects: string[] = [];
38
- selectedProject!: string | null;
39
 
 
 
40
  sessionId: string | null = null;
41
  messages: ChatMessage[] = [];
42
- input = this.fb.control('', Validators.required);
 
 
 
 
 
 
43
  private subs = new Subscription();
44
 
45
- constructor(private fb: FormBuilder, private api: ApiService) {}
 
 
 
46
 
47
- ngOnInit(): void {
48
- /* Proje isimlerini çek → comboya doldur */
49
- const sub = this.api.getChatProjects().subscribe({
50
- next: p => (this.projects = p),
51
- error: () => alert('Projeler yüklenemedi')
52
- });
53
- this.subs.add(sub);
54
  }
55
 
56
- /** Start Chat → /start_session */
57
- startChat(): void {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  if (!this.selectedProject) return;
59
- const sub = this.api.startChat(this.selectedProject).subscribe({
60
- next: res => {
61
- this.sessionId = res.session_id;
62
- this.messages = [{ author: 'assistant', text: res.answer }];
63
- },
64
- error: () => alert('Oturum açılamadı')
65
- });
66
- this.subs.add(sub);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
 
69
- /** Send → /chat */
70
- send(): void {
71
- if (!this.sessionId || this.input.invalid) return;
72
- const text = this.input.value!.trim();
73
- if (!text) return;
74
-
75
- this.messages.push({ author: 'user', text });
76
- this.input.reset();
77
-
78
- const sub = this.api.chat(this.sessionId, text).subscribe({
79
- next: res => this.messages.push({ author: 'assistant', text: res.answer }),
80
- error: () =>
81
- this.messages.push({
82
- author: 'assistant',
83
- text: '⚠️ Mesaj iletilemedi, tekrar deneyin.'
84
- })
85
  });
86
- this.subs.add(sub);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
- ngOnDestroy(): void {
90
- this.subs.unsubscribe();
 
 
 
91
  }
92
- }
 
 
 
 
 
 
 
 
 
 
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
  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';
 
16
  interface ChatMessage {
17
  author: 'user' | 'assistant';
18
  text: string;
19
+ timestamp?: Date;
20
  }
21
 
22
  @Component({
 
31
  MatFormFieldModule,
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
+ }