import { Component, inject, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MinerService } from '../../miner.service';
@Component({
selector: 'app-stats-panel',
standalone: true,
imports: [CommonModule],
template: `
{{ formatHashrate(totalHashrate()) }}
{{ getHashrateUnit(totalHashrate()) }}
Hashrate
{{ totalShares() }}
@if (totalRejected() > 0) {
/ {{ totalRejected() }}
}
Shares
{{ formatUptime(maxUptime()) }}
Uptime
@if (viewMode() === 'single') {
{{ selectedMinerName() }}
} @else {
{{ minerCount() }}
}
{{ viewMode() === 'single' ? 'Worker' : 'Workers' }}
`,
styles: [`
.stats-panel {
display: flex;
align-items: center;
gap: 1.5rem;
padding: 0.75rem 1.5rem;
background: var(--color-surface-100);
border-bottom: 1px solid rgb(37 37 66 / 0.2);
}
.stat-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.stat-icon {
width: 18px;
height: 18px;
flex-shrink: 0;
}
.stat-content {
display: flex;
align-items: baseline;
gap: 0.25rem;
}
.stat-value {
font-size: 0.9375rem;
font-weight: 600;
color: white;
font-family: var(--font-family-mono);
}
.stat-value.pool-name,
.stat-value.single-label {
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: var(--font-family-sans);
font-size: 0.875rem;
}
.stat-value.single-label {
color: var(--color-accent-400);
}
.stat-unit {
font-size: 0.75rem;
color: #94a3b8;
font-weight: 500;
}
.stat-rejected {
font-size: 0.75rem;
color: var(--color-danger-500);
font-family: var(--font-family-mono);
}
.stat-label {
font-size: 0.6875rem;
color: #64748b;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.stat-divider {
width: 1px;
height: 24px;
background: rgb(37 37 66 / 0.3);
}
@media (max-width: 768px) {
.stats-panel {
gap: 0.75rem;
padding: 0.5rem 1rem;
overflow-x: auto;
}
.stat-label {
display: none;
}
}
`]
})
export class StatsPanelComponent {
private minerService = inject(MinerService);
private state = this.minerService.state;
// Use displayedMiners which respects single/multi view mode
miners = this.minerService.displayedMiners;
viewMode = this.minerService.viewMode;
selectedMinerName = this.minerService.selectedMinerName;
totalHashrate = computed(() => {
return this.miners().reduce((sum, m) => sum + (m.full_stats?.hashrate?.total?.[0] || 0), 0);
});
totalShares = computed(() => {
return this.miners().reduce((sum, m) => sum + (m.full_stats?.results?.shares_good || 0), 0);
});
totalRejected = computed(() => {
return this.miners().reduce((sum, m) => {
const total = m.full_stats?.results?.shares_total || 0;
const good = m.full_stats?.results?.shares_good || 0;
return sum + (total - good);
}, 0);
});
maxUptime = computed(() => {
return Math.max(...this.miners().map(m => m.full_stats?.uptime || 0), 0);
});
poolName = computed(() => {
const pools = [...new Set(this.miners()
.map(m => m.full_stats?.connection?.pool?.split(':')[0])
.filter(Boolean))];
if (pools.length === 0) return 'Not connected';
if (pools.length === 1) return pools[0];
return `${pools.length} pools`;
});
minerCount = computed(() => this.miners().length);
formatHashrate(hashrate: number): string {
if (hashrate >= 1000000000) return (hashrate / 1000000000).toFixed(2);
if (hashrate >= 1000000) return (hashrate / 1000000).toFixed(2);
if (hashrate >= 1000) return (hashrate / 1000).toFixed(2);
return hashrate.toFixed(0);
}
getHashrateUnit(hashrate: number): string {
if (hashrate >= 1000000000) return 'GH/s';
if (hashrate >= 1000000) return 'MH/s';
if (hashrate >= 1000) return 'kH/s';
return 'H/s';
}
formatUptime(seconds: number): string {
if (seconds < 60) return `${seconds}s`;
if (seconds < 3600) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}m ${secs}s`;
}
const hours = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
return `${hours}h ${mins}m`;
}
}