Skip to content
Merged
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
10 changes: 8 additions & 2 deletions server/routes/device/removeAll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,10 @@ export default function deviceRemoveAllRoutes(app: FastifyInstance, config: Serv
)

if (allUuids.length === 0) {
stream.error('No deploy history found. Cannot determine which templates are custom.')
stream.error(
'No deploy history found. Cannot determine which templates are custom.',
'Deploy templates first, or pull methods templates from the device to establish tracking.',
)
return
}

Expand Down Expand Up @@ -197,7 +200,10 @@ export default function deviceRemoveAllRoutes(app: FastifyInstance, config: Serv
steps.push(`Saved backup: ${backupFilename} (${Object.keys(fileMap).length} files)`)

if (!existsSync(backupPath)) {
stream.error('Backup verification failed — ZIP was not saved')
stream.error(
'Backup verification failed — ZIP was not saved',
'Check available disk space on the server. The backup must be saved before templates can be removed.',
)
return
}

Expand Down
11 changes: 8 additions & 3 deletions src/components/device/DeviceBackupsCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState, useEffect, useRef, useCallback } from 'react'
import { ErrorDetails } from './ErrorDetails'

interface Props {
deviceId: string | null
Expand Down Expand Up @@ -364,10 +365,14 @@ export function DeviceBackupsCard({ deviceId, deviceName, configured, onStatus:
</div>
)}
{restoreResult && restoreResult.type === 'error' && (
<div className="backup-inline-error">
{restoreResult.message}
<ErrorDetails
error={restoreResult.message}
details={restoreResult.details}
operationName="restore-backup"
className="device-op-result error"
>
<button className="backup-inline-dismiss" onClick={() => setRestoreResult(null)}>&times;</button>
</div>
</ErrorDetails>
)}

{/* Restore preview panel */}
Expand Down
3 changes: 3 additions & 0 deletions src/components/device/DeviceConnectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export function DeviceConnectionCard({ devicesState }: Props) {
error={testResult.error!}
hint={testResult.hint}
rawError={testResult.rawError}
operationName="test-connection"
deviceModel={activeDevice?.deviceModel}
firmwareVersion={activeDevice?.firmwareVersion}
className="device-error"
Expand Down Expand Up @@ -327,6 +328,7 @@ export function DeviceConnectionCard({ devicesState }: Props) {
error={keyResult.error!}
hint={keyResult.hint}
rawError={keyResult.rawError}
operationName="setup-ssh-keys"
deviceModel={activeDevice?.deviceModel}
firmwareVersion={activeDevice?.firmwareVersion}
className="device-error"
Expand Down Expand Up @@ -465,6 +467,7 @@ export function DeviceConnectionCard({ devicesState }: Props) {
error={testResult.error!}
hint={testResult.hint}
rawError={testResult.rawError}
operationName="test-connection"
deviceModel={activeDevice?.deviceModel}
firmwareVersion={activeDevice?.firmwareVersion}
className="device-error"
Expand Down
9 changes: 8 additions & 1 deletion src/components/device/DeviceOpComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function OpButton({
variant = 'primary',
disabled = false,
title,
operationName,
deviceModel,
firmwareVersion,
}: {
Expand All @@ -49,6 +50,7 @@ export function OpButton({
variant?: 'primary' | 'secondary' | 'danger'
disabled?: boolean
title?: string
operationName?: string
deviceModel?: string
firmwareVersion?: string
}) {
Expand All @@ -67,11 +69,16 @@ export function OpButton({
<ProgressBar progress={op.progress} label={loadingLabel} />
)}
{op.result && !op.result.ok && (
<ErrorDetails error={op.result.error} hint={op.result.hint} rawError={op.result.rawError} deviceModel={deviceModel} firmwareVersion={firmwareVersion} />
<ErrorDetails error={op.result.error} hint={op.result.hint} rawError={op.result.rawError} details={op.result.details} operationName={operationName} deviceModel={deviceModel} firmwareVersion={firmwareVersion} />
)}
{op.result?.ok && (
<div className="device-op-result">
<p style={{ margin: 0 }}>{op.result.message}</p>
{op.result.warnings && op.result.warnings.length > 0 && (
<ul className="device-op-warnings">
{op.result.warnings.map((w, i) => <li key={i}>{w}</li>)}
</ul>
)}
</div>
)}
</div>
Expand Down
11 changes: 9 additions & 2 deletions src/components/device/DeviceSyncCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ function SyncStatusSection({ syncStatus, autoRefreshed, deviceModel, firmwareVer

{error && (
<div style={{ marginTop: 8 }}>
<ErrorDetails error={error.message} hint={error.hint} rawError={error.rawError} deviceModel={deviceModel} firmwareVersion={firmwareVersion} />
<ErrorDetails error={error.message} hint={error.hint} rawError={error.rawError} operationName="sync-status" deviceModel={deviceModel} firmwareVersion={firmwareVersion} />
</div>
)}

Expand Down Expand Up @@ -607,6 +607,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
op={pullMethods}
disabled={anyOpRunning}
title={`Download methods templates (official + custom) from ${deviceName}`}
operationName="pull-methods"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand All @@ -617,6 +618,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
variant="secondary"
disabled={anyOpRunning}
title={`Download classic templates from ${deviceName}`}
operationName="pull-classic"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand All @@ -640,6 +642,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
title={selective.showSelector && selective.selectedIds.size === 0
? 'Select at least one template to deploy'
: `Build and push templates to ${deviceName} in methods format — syncs across paired devices`}
operationName="deploy-methods"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand All @@ -650,6 +653,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
variant="secondary"
disabled={anyOpRunning}
title={`Push classic templates to ${deviceName} — single device only, wiped on firmware updates`}
operationName="deploy-classic"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand All @@ -673,6 +677,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
variant="danger"
disabled={anyOpRunning}
title={`Revert ${deviceName} to the state before your last deploy`}
operationName="rollback-methods"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand All @@ -683,6 +688,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
variant="danger"
disabled={anyOpRunning}
title={`Restore ${deviceName} to its pre-app state`}
operationName="rollback-original"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand All @@ -693,6 +699,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,
variant="danger"
disabled={anyOpRunning}
title={`Restore ${deviceName} from the most recent classic template backup`}
operationName="rollback-classic"
deviceModel={deviceModel}
firmwareVersion={firmwareVersion}
/>
Expand Down Expand Up @@ -785,7 +792,7 @@ export function DeviceSyncCard({ deviceId, deviceName, configured, deviceModel,

{removeAll.phase === 'error' && removeAll.errorInfo && (
<div>
<ErrorDetails error={removeAll.errorInfo.message} hint={removeAll.errorInfo.hint} rawError={removeAll.errorInfo.rawError} deviceModel={deviceModel} firmwareVersion={firmwareVersion}>
<ErrorDetails error={removeAll.errorInfo.message} hint={removeAll.errorInfo.hint} rawError={removeAll.errorInfo.rawError} operationName="remove-all" deviceModel={deviceModel} firmwareVersion={firmwareVersion}>
<button
className="device-card-btn device-card-btn-secondary"
onClick={removeAll.reset}
Expand Down
3 changes: 2 additions & 1 deletion src/components/device/deviceOpHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,13 @@ export function useDeviceOp(url: string, options?: { confirmMsg?: string; onSucc
const count = data.count as number | undefined
const message = data.message as string | undefined
const restoredFrom = data.restoredFrom as string | undefined
const warnings = data.warnings as string[] | undefined
const msg =
message ??
((steps ? steps.join(' \u2192 ') : '') ||
(count !== undefined ? `Pulled ${count} templates` : '') ||
(restoredFrom ? `Restored from ${restoredFrom}` : 'Done'))
setResult({ ok: true, message: msg, steps })
setResult({ ok: true, message: msg, steps, warnings })
options?.onSuccess?.()
} catch (e) {
if (e && typeof e === 'object' && 'error' in e) {
Expand Down
12 changes: 12 additions & 0 deletions src/pages/DevicePage.css
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,18 @@
margin: 6px 0 0;
}

.device-op-warnings {
font-size: 12px;
color: var(--color-warning-text, var(--color-text-secondary));
padding-left: 20px;
margin: 6px 0 0;
list-style: disc;
}

.device-op-warnings li {
margin-bottom: 2px;
}

/* ── Backup list ───────────────────────────────────────────────────── */

.device-backup-list {
Expand Down
Loading