import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatCardModule } from '@angular/material/card'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatTableModule } from '@angular/material/table'; import { MatChipsModule } from '@angular/material/chips'; import { MatDividerModule } from '@angular/material/divider'; import { ApiService } from '../../services/api.service'; import { MatSnackBar } from '@angular/material/snack-bar'; interface SparkResponse { type: string; timestamp: Date; request?: any; response?: any; error?: string; } interface SparkProject { project_name: string; version: number; enabled: boolean; status: string; last_accessed: string; base_model: string; has_adapter: boolean; } @Component({ selector: 'app-spark', standalone: true, imports: [ CommonModule, FormsModule, MatCardModule, MatFormFieldModule, MatSelectModule, MatButtonModule, MatIconModule, MatProgressSpinnerModule, MatExpansionModule, MatTableModule, MatChipsModule, MatDividerModule ], template: `
flash_on Spark Integration Manage Spark LLM service integration Select Project {{ project.name }} {{ project.caption ? '- ' + project.caption : '' }} folder
@if (loading) {

Processing request...

} @if (responses.length > 0) {

Response History

@for (response of responses; track response.timestamp) { {{ response.type }} {{ response.timestamp | date:'HH:mm:ss' }} @if (response.request) {

Request:

{{ response.request | json }}
} @if (response.response) {

Response:

@if (response.type === 'Get Project Status' && response.response.projects) {
Project {{ project.project_name }} Version v{{ project.version }} Status {{ project.status }} Enabled {{ project.enabled ? 'check_circle' : 'cancel' }} Base Model {{ project.base_model }} Last Accessed {{ project.last_accessed }}
} @else {
{{ response.response | json }}
}
} @if (response.error) {

Error:

{{ response.error }}
}
}
}
`, styles: [` .spark-container { max-width: 1200px; margin: 0 auto; } mat-card-header { margin-bottom: 24px; mat-card-title { display: flex; align-items: center; gap: 8px; font-size: 24px; mat-icon { font-size: 28px; width: 28px; height: 28px; } } } .project-select { width: 100%; max-width: 400px; margin-bottom: 24px; } .action-buttons { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 24px; button { display: flex; align-items: center; gap: 8px; } } .loading-indicator { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px; p { color: #666; font-size: 14px; } } .section-divider { margin: 32px 0; } .response-list { margin-top: 16px; mat-expansion-panel { margin-bottom: 16px; } mat-panel-title { display: flex; align-items: center; gap: 12px; .timestamp { margin-left: auto; color: #666; font-size: 14px; } } } .response-section { margin: 16px 0; h4 { margin-bottom: 8px; color: #666; } &.error { h4 { color: #f44336; } } } .json-display { background-color: #f5f5f5; padding: 16px; border-radius: 4px; font-family: 'Consolas', 'Monaco', monospace; font-size: 13px; overflow-x: auto; white-space: pre-wrap; word-break: break-word; &.error-text { background-color: #ffebee; color: #c62828; } } .projects-table { width: 100%; background: #fafafa; .model-cell { font-size: 12px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } } mat-chip { font-size: 12px; min-height: 24px; padding: 4px 12px; &.success-chip { background-color: #4caf50; color: white; } &.error-chip { background-color: #f44336; color: white; } } ::ng-deep { .mat-mdc-progress-spinner { --mdc-circular-progress-active-indicator-color: #3f51b5; } } `] }) export class SparkComponent implements OnInit { projects: any[] = []; selectedProject: string = ''; loading = false; responses: SparkResponse[] = []; displayedColumns: string[] = ['project_name', 'version', 'status', 'enabled', 'base_model', 'last_accessed']; constructor( private apiService: ApiService, private snackBar: MatSnackBar ) {} ngOnInit() { this.loadProjects(); } loadProjects() { this.apiService.getProjects().subscribe({ next: (projects) => { this.projects = projects.filter((p: any) => p.enabled && !p.deleted); }, error: (err) => { this.snackBar.open('Failed to load projects', 'Close', { duration: 5000, panelClass: 'error-snackbar' }); } }); } onProjectChange() { // Clear previous responses when project changes this.responses = []; } private addResponse(type: string, request?: any, response?: any, error?: string) { this.responses.unshift({ type, timestamp: new Date(), request, response, error }); // Keep only last 10 responses if (this.responses.length > 10) { this.responses.pop(); } } projectStartup() { if (!this.selectedProject) return; this.loading = true; const request = { project_name: this.selectedProject }; this.apiService.sparkStartup(this.selectedProject).subscribe({ next: (response) => { this.addResponse('Project Startup', request, response); this.snackBar.open(response.message || 'Startup initiated', 'Close', { duration: 3000 }); this.loading = false; }, error: (err) => { this.addResponse('Project Startup', request, null, err.error?.detail || err.message); this.snackBar.open(err.error?.detail || 'Startup failed', 'Close', { duration: 5000, panelClass: 'error-snackbar' }); this.loading = false; } }); } getProjectStatus() { this.loading = true; this.apiService.sparkGetProjects().subscribe({ next: (response) => { this.addResponse('Get Project Status', null, response); this.loading = false; }, error: (err) => { this.addResponse('Get Project Status', null, null, err.error?.detail || err.message); this.snackBar.open(err.error?.detail || 'Failed to get status', 'Close', { duration: 5000, panelClass: 'error-snackbar' }); this.loading = false; } }); } enableProject() { if (!this.selectedProject) return; this.loading = true; const request = { project_name: this.selectedProject }; this.apiService.sparkEnableProject(this.selectedProject).subscribe({ next: (response) => { this.addResponse('Enable Project', request, response); this.snackBar.open(response.message || 'Project enabled', 'Close', { duration: 3000 }); this.loading = false; }, error: (err) => { this.addResponse('Enable Project', request, null, err.error?.detail || err.message); this.snackBar.open(err.error?.detail || 'Enable failed', 'Close', { duration: 5000, panelClass: 'error-snackbar' }); this.loading = false; } }); } disableProject() { if (!this.selectedProject) return; this.loading = true; const request = { project_name: this.selectedProject }; this.apiService.sparkDisableProject(this.selectedProject).subscribe({ next: (response) => { this.addResponse('Disable Project', request, response); this.snackBar.open(response.message || 'Project disabled', 'Close', { duration: 3000 }); this.loading = false; }, error: (err) => { this.addResponse('Disable Project', request, null, err.error?.detail || err.message); this.snackBar.open(err.error?.detail || 'Disable failed', 'Close', { duration: 5000, panelClass: 'error-snackbar' }); this.loading = false; } }); } deleteProject() { if (!this.selectedProject) return; if (!confirm(`Are you sure you want to delete "${this.selectedProject}" from Spark?`)) { return; } this.loading = true; const request = { project_name: this.selectedProject }; this.apiService.sparkDeleteProject(this.selectedProject).subscribe({ next: (response) => { this.addResponse('Delete Project', request, response); this.snackBar.open(response.message || 'Project deleted', 'Close', { duration: 3000 }); this.loading = false; this.selectedProject = ''; }, error: (err) => { this.addResponse('Delete Project', request, null, err.error?.detail || err.message); this.snackBar.open(err.error?.detail || 'Delete failed', 'Close', { duration: 5000, panelClass: 'error-snackbar' }); this.loading = false; } }); } getStatusClass(status: string): string { switch (status) { case 'ready': return 'status-ready'; case 'loading': return 'status-loading'; case 'error': return 'status-error'; case 'unloaded': return 'status-unloaded'; default: return ''; } } trackByTimestamp(index: number, response: SparkResponse): Date { return response.timestamp; } }