Add visual feedback for async operations across UI components: - Profile list: Start, Delete, and Save buttons show spinners during actions - Profile create: Create Profile button shows spinner during submission - Nodes page: Initialize Node, Add Peer, Ping, and Remove buttons show spinners Buttons are disabled while their action is in progress to prevent duplicate submissions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
117 lines
4.8 KiB
HTML
117 lines
4.8 KiB
HTML
<div class="flex flex-col gap-4">
|
|
<h5 class="text-lg font-semibold">Existing Profiles</h5>
|
|
@for(profile of state().profiles; track profile.id) {
|
|
<div class="card p-4">
|
|
@if(editingProfile && editingProfile.id === profile.id) {
|
|
<div class="flex flex-col gap-4">
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm font-medium text-slate-300">Profile Name</label>
|
|
<input
|
|
type="text"
|
|
class="input"
|
|
[value]="editingProfile.name"
|
|
(input)="onNameInput($event)">
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm font-medium text-slate-300">Miner Type</label>
|
|
<select
|
|
class="select"
|
|
[value]="editingProfile.minerType"
|
|
(change)="onMinerTypeChange($event)">
|
|
@for(miner of state().manageableMiners; track miner.name) {
|
|
<option [value]="miner.name" [selected]="miner.name === editingProfile.minerType">
|
|
{{ miner.name }}
|
|
</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm font-medium text-slate-300">Pool Address</label>
|
|
<input
|
|
type="text"
|
|
class="input"
|
|
[value]="editingProfile.config.pool"
|
|
(input)="onPoolInput($event)">
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-sm font-medium text-slate-300">Wallet Address</label>
|
|
<input
|
|
type="text"
|
|
class="input"
|
|
[value]="editingProfile.config.wallet"
|
|
(input)="onWalletInput($event)">
|
|
</div>
|
|
<div class="flex gap-6 items-center">
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="checkbox"
|
|
class="w-4 h-4 rounded border-surface-50 text-accent-500 focus:ring-accent-500"
|
|
[checked]="editingProfile.config.tls"
|
|
(change)="onTlsChange($event)">
|
|
<span class="text-sm">TLS</span>
|
|
</label>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input
|
|
type="checkbox"
|
|
class="w-4 h-4 rounded border-surface-50 text-accent-500 focus:ring-accent-500"
|
|
[checked]="editingProfile.config.hugePages"
|
|
(change)="onHugePagesChange($event)">
|
|
<span class="text-sm">Huge Pages</span>
|
|
</label>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<button
|
|
class="btn btn-primary btn-sm"
|
|
[disabled]="actionInProgress() === 'save-' + editingProfile.id"
|
|
(click)="updateProfile()">
|
|
@if (actionInProgress() === 'save-' + editingProfile.id) {
|
|
<div class="animate-spin w-4 h-4 border-2 border-white border-t-transparent rounded-full mr-1"></div>
|
|
Saving...
|
|
} @else {
|
|
Save
|
|
}
|
|
</button>
|
|
<button
|
|
class="btn btn-secondary btn-sm"
|
|
[disabled]="actionInProgress() === 'save-' + editingProfile.id"
|
|
(click)="cancelEdit()">Cancel</button>
|
|
</div>
|
|
</div>
|
|
} @else {
|
|
<div class="flex justify-between items-center">
|
|
<span class="font-medium">{{ profile.name }} <span class="text-slate-400">({{ profile.minerType }})</span></span>
|
|
<div class="flex gap-2">
|
|
<button
|
|
class="btn btn-primary btn-xs"
|
|
[disabled]="actionInProgress() === 'start-' + profile.id"
|
|
(click)="startMiner(profile.id)">
|
|
@if (actionInProgress() === 'start-' + profile.id) {
|
|
<div class="animate-spin w-3 h-3 border-2 border-white border-t-transparent rounded-full mr-1"></div>
|
|
Starting...
|
|
} @else {
|
|
Start
|
|
}
|
|
</button>
|
|
<button
|
|
class="btn btn-secondary btn-xs"
|
|
[disabled]="actionInProgress() !== null"
|
|
(click)="editProfile(profile)">Edit</button>
|
|
<button
|
|
class="btn btn-danger btn-xs"
|
|
[disabled]="actionInProgress() === 'delete-' + profile.id"
|
|
(click)="deleteProfile(profile.id)">
|
|
@if (actionInProgress() === 'delete-' + profile.id) {
|
|
<div class="animate-spin w-3 h-3 border-2 border-white border-t-transparent rounded-full mr-1"></div>
|
|
Deleting...
|
|
} @else {
|
|
Delete
|
|
}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
} @empty {
|
|
<p class="text-slate-400 italic">No profiles created yet.</p>
|
|
}
|
|
</div>
|