import { Component, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { MinerService } from '../../miner.service'; import { NotificationService } from '../../notification.service'; import { ProfileCreateComponent } from '../../profile-create.component'; @Component({ selector: 'app-profiles', standalone: true, imports: [CommonModule, ProfileCreateComponent], template: `
@if (showCreateForm()) {
} @if (profiles().length > 0) {
@for (profile of profiles(); track profile.id) {
@if (editingProfileId() === profile.id) {
} @else {

{{ profile.name }}

{{ profile.minerType }}
@if (isRunning(profile.id)) {
Running
}
Pool {{ profile.config?.pool || 'Not set' }}
Wallet {{ profile.config?.wallet || 'Not set' }}
@if (!isRunning(profile.id)) { } @else { }
}
}
} @else if (!showCreateForm()) {

No Profiles Yet

Create a profile to save your mining configuration.

}
`, styles: [` .profiles-page { display: flex; flex-direction: column; gap: 1.5rem; } .page-header { display: flex; align-items: flex-start; justify-content: space-between; } .page-header h2 { font-size: 1.25rem; font-weight: 600; color: white; } .page-header p { margin-top: 0.25rem; font-size: 0.875rem; color: #64748b; } .btn { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0.5rem 1rem; border-radius: 0.375rem; font-size: 0.875rem; font-weight: 500; cursor: pointer; transition: all 0.15s ease; border: none; } .btn-primary { background: var(--color-accent-500); color: #0f0f1a; } .btn-primary:hover { background: rgb(0 212 255 / 0.8); } .create-form-container { background: var(--color-surface-100); border-radius: 0.5rem; border: 1px solid rgb(37 37 66 / 0.2); padding: 1.5rem; } .profiles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 1rem; } .profile-card { padding: 1.25rem; background: var(--color-surface-100); border-radius: 0.5rem; border: 1px solid rgb(37 37 66 / 0.2); transition: border-color 0.15s ease; } .profile-card.active { border-color: rgb(16 185 129 / 0.3); } .profile-card.editing { border-color: var(--color-accent-500); } .profile-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 1rem; } .header-actions { display: flex; align-items: center; gap: 0.5rem; } .icon-btn { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; background: transparent; border: none; border-radius: 0.25rem; color: #94a3b8; cursor: pointer; transition: all 0.15s ease; } .icon-btn:hover:not(:disabled) { background: rgb(37 37 66 / 0.5); color: white; } .icon-btn:disabled { opacity: 0.4; cursor: not-allowed; } .edit-form { display: flex; flex-direction: column; gap: 1rem; } .form-group { display: flex; flex-direction: column; gap: 0.375rem; } .form-group label { font-size: 0.75rem; font-weight: 500; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em; } .form-input { padding: 0.5rem 0.75rem; background: var(--color-surface-200); border: 1px solid rgb(37 37 66 / 0.3); border-radius: 0.375rem; color: white; font-size: 0.875rem; } .form-input:focus { outline: none; border-color: var(--color-accent-500); } .form-input::placeholder { color: #64748b; } .edit-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; } .btn-outline { background: transparent; border: 1px solid rgb(37 37 66 / 0.3); color: #94a3b8; } .btn-outline:hover { background: rgb(37 37 66 / 0.3); color: white; } .profile-info h3 { font-size: 1rem; font-weight: 600; color: white; } .profile-miner { display: inline-block; margin-top: 0.25rem; padding: 0.125rem 0.375rem; background: rgb(0 212 255 / 0.1); border-radius: 0.25rem; font-size: 0.6875rem; color: var(--color-accent-500); text-transform: uppercase; } .running-badge { display: flex; align-items: center; gap: 0.375rem; padding: 0.25rem 0.5rem; background: rgb(16 185 129 / 0.1); border-radius: 0.25rem; font-size: 0.75rem; color: var(--color-success-500); } .pulse-dot { width: 6px; height: 6px; background: var(--color-success-500); border-radius: 50%; animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } .profile-details { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.75rem 0; border-top: 1px solid rgb(37 37 66 / 0.2); border-bottom: 1px solid rgb(37 37 66 / 0.2); margin-bottom: 1rem; } .detail-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; } .detail-label { font-size: 0.75rem; color: #64748b; } .detail-value { font-size: 0.8125rem; color: #e2e8f0; font-family: var(--font-family-mono); text-align: right; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .profile-actions { display: flex; align-items: center; gap: 0.5rem; } .action-btn { display: flex; align-items: center; gap: 0.375rem; padding: 0.5rem 0.75rem; background: transparent; border: 1px solid rgb(37 37 66 / 0.3); border-radius: 0.375rem; font-size: 0.8125rem; cursor: pointer; transition: all 0.15s ease; } .action-btn.start { color: var(--color-success-500); border-color: rgb(16 185 129 / 0.3); } .action-btn.start:hover { background: rgb(16 185 129 / 0.1); } .action-btn.stop { color: var(--color-warning-500); border-color: rgb(245 158 11 / 0.3); } .action-btn.stop:hover { background: rgb(245 158 11 / 0.1); } .action-btn.delete { color: var(--color-danger-500); border-color: rgb(239 68 68 / 0.3); margin-left: auto; } .action-btn.delete:hover:not(:disabled) { background: rgb(239 68 68 / 0.1); } .action-btn:disabled { opacity: 0.5; cursor: not-allowed; } .empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 4rem 2rem; text-align: center; } .empty-state h3 { margin-top: 1rem; font-size: 1.125rem; font-weight: 600; color: white; } .empty-state p { margin-top: 0.5rem; color: #64748b; font-size: 0.875rem; } .mt-4 { margin-top: 1rem; } .animate-spin { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } /* Mobile responsive styles */ @media (max-width: 768px) { .page-header { flex-direction: column; gap: 1rem; } .page-header .btn { width: 100%; justify-content: center; } .profiles-grid { grid-template-columns: 1fr; } .profile-actions { flex-wrap: wrap; } .action-btn { flex: 1; min-width: 80px; justify-content: center; } .action-btn.delete { margin-left: 0; } .empty-state { padding: 2rem 1rem; } } `] }) export class ProfilesComponent { private minerService = inject(MinerService); private notifications = inject(NotificationService); state = this.minerService.state; showCreateForm = signal(false); editingProfileId = signal(null); // Loading states startingProfile = signal(null); stoppingProfile = signal(null); deletingProfile = signal(null); savingProfile = signal(null); profiles = () => this.state().profiles; isRunning(profileId: string): boolean { return this.state().runningMiners.some(m => m.profile_id === profileId); } getProfileName(profileId: string): string { return this.state().profiles.find(p => p.id === profileId)?.name || 'Profile'; } startProfile(profileId: string) { const name = this.getProfileName(profileId); this.startingProfile.set(profileId); this.minerService.startMiner(profileId).subscribe({ next: () => { this.startingProfile.set(null); this.notifications.success(`${name} started successfully`, 'Miner Started'); }, error: (err) => { this.startingProfile.set(null); console.error('Failed to start profile:', err); this.notifications.error(`Failed to start ${name}: ${err.message || 'Unknown error'}`, 'Start Failed'); } }); } stopProfile(profileId: string) { const miner = this.state().runningMiners.find(m => m.profile_id === profileId); if (miner) { this.stoppingProfile.set(profileId); this.minerService.stopMiner(miner.name).subscribe({ next: () => { this.stoppingProfile.set(null); this.notifications.success(`${miner.name} stopped successfully`, 'Miner Stopped'); }, error: (err) => { this.stoppingProfile.set(null); console.error('Failed to stop miner:', err); this.notifications.error(`Failed to stop ${miner.name}: ${err.message || 'Unknown error'}`, 'Stop Failed'); } }); } } deleteProfile(profileId: string) { const name = this.getProfileName(profileId); if (confirm('Are you sure you want to delete this profile?')) { this.deletingProfile.set(profileId); this.minerService.deleteProfile(profileId).subscribe({ next: () => { this.deletingProfile.set(null); this.notifications.success(`${name} deleted successfully`, 'Profile Deleted'); }, error: (err) => { this.deletingProfile.set(null); console.error('Failed to delete profile:', err); this.notifications.error(`Failed to delete ${name}: ${err.message || 'Unknown error'}`, 'Delete Failed'); } }); } } onProfileCreated() { this.showCreateForm.set(false); this.notifications.success('Profile created successfully', 'Profile Created'); } startEdit(profileId: string) { this.editingProfileId.set(profileId); } cancelEdit() { this.editingProfileId.set(null); } saveProfile(profileId: string, name: string, pool: string, wallet: string) { const profile = this.state().profiles.find(p => p.id === profileId); if (!profile) return; this.savingProfile.set(profileId); const updatedProfile = { ...profile, name: name.trim() || profile.name, config: { ...profile.config, pool: pool.trim() || profile.config?.pool, wallet: wallet.trim() || profile.config?.wallet } }; this.minerService.updateProfile(updatedProfile).subscribe({ next: () => { this.savingProfile.set(null); this.editingProfileId.set(null); this.notifications.success(`${name} updated successfully`, 'Profile Updated'); }, error: (err) => { this.savingProfile.set(null); console.error('Failed to update profile:', err); this.notifications.error(`Failed to update profile: ${err.message || 'Unknown error'}`, 'Update Failed'); } }); } }