Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions src/components/data/ConnectionStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { cn } from '@/lib/utils';
import { StatusDot } from '@/components/ui/status-dot';

type ConnectionState = 'connected' | 'connecting' | 'disconnected' | 'error';

Expand All @@ -17,23 +18,18 @@ export function ConnectionStatus({
label,
className,
}: ConnectionStatusProps) {
const stateConfig: Record<ConnectionState, { color: string; text: string }> = {
connected: { color: 'bg-green-500', text: 'Connected' },
connecting: { color: 'bg-yellow-500 animate-pulse', text: 'Connecting' },
disconnected: { color: 'bg-muted-foreground', text: 'Disconnected' },
error: { color: 'bg-red-500', text: 'Error' },
const stateConfig: Record<ConnectionState, { status: 'connected' | 'connecting' | 'disconnected' | 'idle'; text: string }> = {
connected: { status: 'connected', text: 'Connected' },
connecting: { status: 'connecting', text: 'Connecting' },
disconnected: { status: 'idle', text: 'Disconnected' },
error: { status: 'disconnected', text: 'Error' },
};

const config = stateConfig[state];

return (
<div className={cn('flex items-center gap-2', className)}>
<div
className={cn(
'h-2 w-2 rounded-full shadow-sm',
config.color
)}
/>
<StatusDot status={config.status} size="sm" className="shadow-sm" />
<span className="text-sm text-muted-foreground">
{label || config.text}
</span>
Expand Down
13 changes: 3 additions & 10 deletions src/components/layout/Shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { AppMode, AppFeatures } from '@/types/api';
import { getAppFeatures } from '@/types/api';
import { useUiConfig } from '@/hooks/useUiConfig';
import { PoolIcon } from '@/components/ui/pool-icon';
import { StatusDot } from '@/components/ui/status-dot';

function useTheme() {
const [isDark, setIsDark] = useState(() => {
Expand Down Expand Up @@ -173,20 +174,12 @@ export function Shell({
<>
{/* Mobile: dot + uptime only (no status text to save space) */}
<span className="flex sm:hidden items-center gap-2 text-xs text-muted-foreground min-w-0">
<span className={cn('h-2 w-2 rounded-full shrink-0', {
'bg-green-500': connectionStatus === 'connected',
'bg-red-500': connectionStatus === 'disconnected',
'bg-yellow-500 animate-pulse': connectionStatus === 'connecting',
})} />
<StatusDot status={connectionStatus} size="sm" />
<span className="truncate">Uptime: {formatUptime(uptime ?? 0)}</span>
</span>
{/* Desktop: dot + full status text + uptime */}
<span className="hidden sm:flex items-center gap-2 text-xs text-muted-foreground shrink-0">
<span className={cn('h-2 w-2 rounded-full shrink-0', {
'bg-green-500': connectionStatus === 'connected',
'bg-red-500': connectionStatus === 'disconnected',
'bg-yellow-500 animate-pulse': connectionStatus === 'connecting',
})} />
<StatusDot status={connectionStatus} size="sm" />
{connectionStatus === 'connected' ? (
<span className="inline-flex min-w-0 items-center gap-1.5">
<span className="truncate">{connectedStatusLabel}</span>
Expand Down
9 changes: 3 additions & 6 deletions src/components/mining/AdvancedMiningConfigForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '@sv2-ui/shared';

import { Switch } from '@/components/ui/switch';
import { FieldError } from '@/components/ui/field-error';

export interface AdvancedMiningConfigValues {
sharesPerMinute: string;
Expand Down Expand Up @@ -124,9 +125,7 @@ export function AdvancedMiningConfigForm({
className="h-9 w-full rounded-lg border border-input bg-background px-3 text-sm outline-none transition-all focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15"
/>
{!sharesPerMinuteValid ? (
<p id={sharesPerMinuteDescriptionId} className="mt-1 text-xs text-destructive">
Enter a value greater than 0.
</p>
<FieldError message="Enter a value greater than 0." className="mt-1" id={sharesPerMinuteDescriptionId} />
) : (
<p id={sharesPerMinuteDescriptionId} className="mt-1 text-xs text-muted-foreground">
Target share rate for variable difficulty.
Expand All @@ -151,9 +150,7 @@ export function AdvancedMiningConfigForm({
className="h-9 w-full rounded-lg border border-input bg-background px-3 text-sm outline-none transition-all focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15"
/>
{!downstreamExtranonce2SizeValid ? (
<p id={downstreamExtranonce2SizeDescriptionId} className="mt-1 text-xs text-destructive">
Enter a whole number from 1 to 65,535.
</p>
<FieldError message="Enter a whole number from 1 to 65,535." className="mt-1" id={downstreamExtranonce2SizeDescriptionId} />
) : (
<p id={downstreamExtranonce2SizeDescriptionId} className="mt-1 text-xs text-muted-foreground">
Bytes assigned to downstream SV1 miners.
Expand Down
16 changes: 7 additions & 9 deletions src/components/pools/PoolIdentityFields.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { AlertCircle } from 'lucide-react';
import { Alert } from '@/components/ui/alert';
import { FieldError } from '@/components/ui/field-error';
import {
shouldAggregateTranslatorChannels,
type BitcoinNetwork,
Expand Down Expand Up @@ -64,13 +65,12 @@ export function PoolIdentityFields({
<span className="sr-only">(required)</span>
</label>
{showBraiinsUsernameWarning && (
<div className="mb-3 flex gap-3 rounded-xl bg-warning/[0.08] p-4 text-sm text-warning" role="alert">
<AlertCircle className="mt-0.5 h-4 w-4 flex-shrink-0" aria-hidden="true" />
<Alert variant="warning" className="mb-3">
<p>
Use the exact username from your Braiins Pool account. If this value does not match an existing
Braiins account, the pool connection will not be established properly.
</p>
</div>
</Alert>
)}
<input
id={`${idPrefix}-identity`}
Expand All @@ -82,7 +82,7 @@ export function PoolIdentityFields({
autoComplete="off"
className="w-full h-10 px-3 rounded-lg border border-input bg-background focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15 outline-none transition-all font-mono text-sm"
/>
{error && <p className="text-xs text-destructive mt-1">{error}</p>}
<FieldError message={error} />
<p className="text-xs text-muted-foreground mt-2">
{miningMode === 'solo'
? 'Bitcoin address used by the pool for solo mining payouts.'
Expand Down Expand Up @@ -147,9 +147,7 @@ function SriPoolIdentityFields({
autoComplete="off"
className="w-full h-10 px-3 rounded-lg border border-input bg-background focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15 outline-none transition-all font-mono text-sm"
/>
{getBitcoinAddressError(payoutAddress, network) && (
<p className="text-xs text-destructive mt-1">{getBitcoinAddressError(payoutAddress, network)}</p>
)}
<FieldError message={getBitcoinAddressError(payoutAddress, network)} />
<p className="text-xs text-muted-foreground mt-2">
Used with worker and donation settings to build this pool identity.
</p>
Expand Down Expand Up @@ -215,7 +213,7 @@ function SriPoolIdentityFields({
</p>
</div>

{identityError && <p className="text-xs text-destructive">{identityError}</p>}
<FieldError message={identityError} />
</div>
);
}
3 changes: 2 additions & 1 deletion src/components/pools/PoolPriorityEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useRef, useState } from 'react';
import { ArrowDown, ArrowUp, GripVertical, X } from 'lucide-react';
import { DEFAULT_POOL_PORT, type MiningMode, type PoolConfig } from '@sv2-ui/shared';
import { FieldError } from '@/components/ui/field-error';
import { PoolIcon } from '@/components/ui/pool-icon';
import {
createEmptyCustomPool,
Expand Down Expand Up @@ -380,7 +381,7 @@ function CustomPoolFields({
pubkeyError ? 'border-destructive focus-visible:border-destructive' : 'border-input focus-visible:border-primary'
}`}
/>
{pubkeyError && <p className="mt-1 text-xs text-destructive">{pubkeyError}</p>}
<FieldError message={pubkeyError} />
</div>
</div>
</div>
Expand Down
28 changes: 13 additions & 15 deletions src/components/settings/ConfigurationTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ import {
Check,
X,
} from 'lucide-react';

import { FieldError } from '@/components/ui/field-error';
import { StatusDot } from '@/components/ui/status-dot';
const SETUP_TARGET_STEP_STORAGE_KEY = 'sv2-ui-setup-target-step';

type EditingField = null | 'pools' | 'mode' | 'signature' | 'hashrate' | 'telemetry' | 'advanced';
Expand Down Expand Up @@ -356,10 +357,10 @@ export function ConfigurationTab() {
const updateEditPoolIdentity = (index: number, nextPool: PoolConfig) => {
setEditPools((currentPools) => currentPools
? normalizePoolPriorityIdentities(
currentPools.map((pool, poolIndex) => poolIndex === index ? nextPool : pool),
currentPools[0],
activeMiningMode,
)
currentPools.map((pool, poolIndex) => poolIndex === index ? nextPool : pool),
currentPools[0],
activeMiningMode,
)
: null);
};

Expand All @@ -370,7 +371,7 @@ export function ConfigurationTab() {
<CardContent className="pt-6">
<div className="flex flex-wrap items-center justify-between gap-4">
<div className="flex min-w-0 items-center gap-3">
<div className={`h-3 w-3 shrink-0 rounded-full ${isRunning ? 'bg-green-500' : 'bg-muted-foreground'}`} />
<StatusDot status={isRunning ? 'connected' : 'idle'} size="lg" />
<div className="min-w-0">
<p className="font-medium">{isRunning ? 'Services Running' : 'Services Stopped'}</p>
<p className="text-sm text-muted-foreground">
Expand Down Expand Up @@ -525,11 +526,10 @@ export function ConfigurationTab() {
key={m}
type="button"
onClick={() => setEditMode(m)}
className={`px-4 py-2 rounded-lg border text-sm font-medium transition-all ${
editMode === m
className={`px-4 py-2 rounded-lg border text-sm font-medium transition-all ${editMode === m
? 'border-primary bg-primary/[0.04] text-primary'
: 'border-border bg-card hover:border-primary/45'
}`}
}`}
>
{m === 'jd' ? 'Job Declaration (Custom Templates)' : 'Pool Templates'}
</button>
Expand Down Expand Up @@ -687,8 +687,8 @@ export function ConfigurationTab() {
placeholder="Miner signature"
className="w-full h-10 px-3 rounded-lg border border-input bg-background font-mono text-sm focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15 outline-none transition-all"
/>
{editSignature && getIdentifierError(editSignature) && (
<p className="text-xs text-destructive mt-1">{getIdentifierError(editSignature)}</p>
{editSignature && (
<FieldError message={getIdentifierError(editSignature)} />
)}
<p className="text-xs text-muted-foreground mt-2">
Miner-chosen tag shown in coinbase transactions on block explorers.
Expand Down Expand Up @@ -766,9 +766,7 @@ export function ConfigurationTab() {
autoComplete="off"
className="w-full h-9 px-3 rounded-lg border border-input bg-background font-mono text-sm focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/15 outline-none transition-all"
/>
{minerTelemetryCidrError && (
<p className="text-xs text-destructive mt-1">{minerTelemetryCidrError}</p>
)}
<FieldError message={minerTelemetryCidrError} />
<p className="text-xs text-muted-foreground mt-2">
Private LAN subnet where miners expose their web/API interface.
</p>
Expand Down Expand Up @@ -977,4 +975,4 @@ function PoolSummary({
</div>
</div>
);
}
}
Loading
Loading