diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index 4002d38..b998c4b 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -1,23 +1,18 @@ import { TestBed } from '@angular/core/testing'; -import { App } from './app'; +import { provideHttpClient } from '@angular/common/http'; +import { SniderMining } from './app'; -describe('App', () => { +describe('SniderMining', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [App], + imports: [SniderMining], + providers: [provideHttpClient()] }).compileComponents(); }); it('should create the app', () => { - const fixture = TestBed.createComponent(App); + const fixture = TestBed.createComponent(SniderMining); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); - - it('should render title', () => { - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ui'); - }); }); diff --git a/ui/src/app/components/api-status/api-status.component.ts b/ui/src/app/components/api-status/api-status.component.ts new file mode 100644 index 0000000..d859610 --- /dev/null +++ b/ui/src/app/components/api-status/api-status.component.ts @@ -0,0 +1,106 @@ +import { Component, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MinerService } from '../../miner.service'; + +@Component({ + selector: 'app-api-status', + standalone: true, + imports: [CommonModule], + template: ` + @if (!minerService.apiAvailable()) { +
+ } + `, + styles: [` + .api-error-banner { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 9998; + background: linear-gradient(135deg, #991b1b 0%, #7f1d1d 100%); + border-bottom: 1px solid rgb(239 68 68 / 0.3); + padding: 0.75rem 1rem; + } + + .banner-content { + display: flex; + align-items: center; + gap: 1rem; + max-width: 1200px; + margin: 0 auto; + } + + .banner-icon { + width: 1.5rem; + height: 1.5rem; + color: #fca5a5; + flex-shrink: 0; + } + + .banner-text { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.125rem; + } + + .banner-title { + font-weight: 600; + font-size: 0.875rem; + color: #fef2f2; + } + + .banner-message { + font-size: 0.8125rem; + color: #fecaca; + } + + .retry-btn { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.5rem 1rem; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 0.375rem; + color: white; + font-size: 0.8125rem; + font-weight: 500; + cursor: pointer; + transition: all 0.15s ease; + } + + .retry-btn:hover { + background: rgba(255, 255, 255, 0.2); + } + + .retry-btn svg { + width: 1rem; + height: 1rem; + } + `] +}) +export class ApiStatusComponent { + minerService = inject(MinerService); + + retry() { + this.minerService.forceRefreshState(); + } +} diff --git a/ui/src/app/components/sidebar/sidebar.component.spec.ts b/ui/src/app/components/sidebar/sidebar.component.spec.ts new file mode 100644 index 0000000..1de9d19 --- /dev/null +++ b/ui/src/app/components/sidebar/sidebar.component.spec.ts @@ -0,0 +1,122 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { SidebarComponent } from './sidebar.component'; + +describe('SidebarComponent', () => { + let component: SidebarComponent; + let fixture: ComponentFixture