The gateway card showed token status as read-only (set/none) with no way to update it. Users with a registered gateway but missing token had to delete and re-add the gateway. Add [edit] link next to the token indicator that expands an inline password input. Supports Enter to save, Escape to cancel. Calls PUT /api/gateways with the token field (already supported by API).
This commit is contained in:
parent
60f6dc07a1
commit
5bc5737d56
|
|
@ -160,6 +160,15 @@ export function MultiGatewayPanel() {
|
|||
fetchHistory()
|
||||
}
|
||||
|
||||
const updateToken = async (gw: Gateway, token: string) => {
|
||||
await fetch('/api/gateways', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: gw.id, token }),
|
||||
})
|
||||
fetchGateways()
|
||||
}
|
||||
|
||||
const connectTo = async (gw: Gateway) => {
|
||||
try {
|
||||
const res = await fetch('/api/gateways/connect', {
|
||||
|
|
@ -285,6 +294,7 @@ export function MultiGatewayPanel() {
|
|||
onDelete={() => deleteGateway(gw.id)}
|
||||
onConnect={() => connectTo(gw)}
|
||||
onProbe={() => probeGateway(gw)}
|
||||
onUpdateToken={(token) => updateToken(gw, token)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -437,7 +447,7 @@ export function MultiGatewayPanel() {
|
|||
)
|
||||
}
|
||||
|
||||
function GatewayCard({ gateway, health, historyEntries = [], isProbing, isCurrentlyConnected, onSetPrimary, onDelete, onConnect, onProbe }: {
|
||||
function GatewayCard({ gateway, health, historyEntries = [], isProbing, isCurrentlyConnected, onSetPrimary, onDelete, onConnect, onProbe, onUpdateToken }: {
|
||||
gateway: Gateway
|
||||
health?: GatewayHealthProbe
|
||||
historyEntries?: GatewayHealthLogEntry[]
|
||||
|
|
@ -447,8 +457,11 @@ function GatewayCard({ gateway, health, historyEntries = [], isProbing, isCurren
|
|||
onDelete: () => void
|
||||
onConnect: () => void
|
||||
onProbe: () => void
|
||||
onUpdateToken: (token: string) => void
|
||||
}) {
|
||||
const t = useTranslations('multiGateway')
|
||||
const [editingToken, setEditingToken] = useState(false)
|
||||
const [tokenInput, setTokenInput] = useState('')
|
||||
const statusColors: Record<string, string> = {
|
||||
online: 'bg-green-500',
|
||||
offline: 'bg-red-500',
|
||||
|
|
@ -487,10 +500,54 @@ function GatewayCard({ gateway, health, historyEntries = [], isProbing, isCurren
|
|||
</div>
|
||||
<div className="flex items-center gap-4 mt-1.5 text-xs text-muted-foreground">
|
||||
<span className="font-mono">{gateway.host}:{gateway.port}</span>
|
||||
<span>{t('token')}: {gateway.token_set ? t('tokenSet') : t('tokenNone')}</span>
|
||||
<button
|
||||
onClick={() => { setEditingToken(!editingToken); setTokenInput('') }}
|
||||
className="hover:text-foreground transition-colors cursor-pointer"
|
||||
title={gateway.token_set ? 'Change gateway token' : 'Set gateway token'}
|
||||
>
|
||||
{t('token')}: {gateway.token_set ? t('tokenSet') : t('tokenNone')} [edit]
|
||||
</button>
|
||||
{gateway.latency != null && <span>{t('latency')}: {gateway.latency}ms</span>}
|
||||
<span>{t('last')}: {lastSeen}</span>
|
||||
</div>
|
||||
{editingToken && (
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<input
|
||||
type="password"
|
||||
value={tokenInput}
|
||||
onChange={e => setTokenInput(e.target.value)}
|
||||
placeholder="Paste gateway token..."
|
||||
className="flex-1 px-2 py-1 text-xs bg-secondary border border-border rounded font-mono"
|
||||
autoFocus
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter' && tokenInput.trim()) {
|
||||
onUpdateToken(tokenInput.trim())
|
||||
setEditingToken(false)
|
||||
setTokenInput('')
|
||||
} else if (e.key === 'Escape') {
|
||||
setEditingToken(false)
|
||||
setTokenInput('')
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => { onUpdateToken(tokenInput.trim()); setEditingToken(false); setTokenInput('') }}
|
||||
disabled={!tokenInput.trim()}
|
||||
size="xs"
|
||||
className="text-2xs"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => { setEditingToken(false); setTokenInput('') }}
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-2xs"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{health?.gateway_version && (
|
||||
<div className="mt-1 text-2xs text-muted-foreground">
|
||||
{t('gatewayVersion')}: <span className="font-mono text-foreground/80">{health.gateway_version}</span>
|
||||
|
|
|
|||
Loading…
Reference in New Issue