Fix API token generation UX
This commit is contained in:
parent
1d580c658d
commit
e2a0fbe816
2 changed files with 53 additions and 51 deletions
|
|
@ -11,6 +11,7 @@ import type { DockerHost } from '@/types/api';
|
||||||
interface APITokenManagerProps {
|
interface APITokenManagerProps {
|
||||||
currentTokenHint?: string;
|
currentTokenHint?: string;
|
||||||
onTokensChanged?: () => void;
|
onTokensChanged?: () => void;
|
||||||
|
refreshing?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const APITokenManager: Component<APITokenManagerProps> = (props) => {
|
export const APITokenManager: Component<APITokenManagerProps> = (props) => {
|
||||||
|
|
@ -67,16 +68,12 @@ export const APITokenManager: Component<APITokenManagerProps> = (props) => {
|
||||||
const trimmedName = nameInput().trim() || undefined;
|
const trimmedName = nameInput().trim() || undefined;
|
||||||
const { token, record } = await SecurityAPI.createToken(trimmedName);
|
const { token, record } = await SecurityAPI.createToken(trimmedName);
|
||||||
|
|
||||||
console.log('✅ Token generated:', { token, record });
|
|
||||||
|
|
||||||
setTokens((prev) => [record, ...prev]);
|
setTokens((prev) => [record, ...prev]);
|
||||||
setNewTokenValue(token);
|
setNewTokenValue(token);
|
||||||
setNewTokenRecord(record);
|
setNewTokenRecord(record);
|
||||||
setNameInput('');
|
setNameInput('');
|
||||||
|
|
||||||
console.log('✅ State updated, newTokenValue:', token);
|
showSuccess('New API token generated. Copy it below while it is still visible.');
|
||||||
|
|
||||||
showSuccess('New API token generated! Scroll up to see it!');
|
|
||||||
props.onTokensChanged?.();
|
props.onTokensChanged?.();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -88,10 +85,6 @@ export const APITokenManager: Component<APITokenManagerProps> = (props) => {
|
||||||
console.warn('Unable to persist API token in localStorage', storageErr);
|
console.warn('Unable to persist API token in localStorage', storageErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to top to show the token
|
|
||||||
setTimeout(() => {
|
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
||||||
}, 100);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to generate API token', err);
|
console.error('Failed to generate API token', err);
|
||||||
showError('Failed to generate API token');
|
showError('Failed to generate API token');
|
||||||
|
|
@ -190,6 +183,16 @@ export const APITokenManager: Component<APITokenManagerProps> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-6 space-y-6">
|
<div class="p-6 space-y-6">
|
||||||
|
<Show when={props.refreshing}>
|
||||||
|
<div class="flex items-center gap-2 rounded-md bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 px-3 py-2 text-xs text-blue-800 dark:text-blue-200">
|
||||||
|
<svg class="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke-width="4" stroke="currentColor" />
|
||||||
|
<path class="opacity-75" d="M4 12a8 8 0 018-8" stroke-width="4" stroke-linecap="round" stroke="currentColor" />
|
||||||
|
</svg>
|
||||||
|
<span>Refreshing security status…</span>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
{/* CRITICAL: Show generated token FIRST and PROMINENTLY */}
|
{/* CRITICAL: Show generated token FIRST and PROMINENTLY */}
|
||||||
<Show when={newTokenValue()}>
|
<Show when={newTokenValue()}>
|
||||||
<div class="space-y-4 border-4 border-green-500 dark:border-green-600 rounded-lg p-5 bg-green-50 dark:bg-green-900/30">
|
<div class="space-y-4 border-4 border-green-500 dark:border-green-600 rounded-lg p-5 bg-green-50 dark:bg-green-900/30">
|
||||||
|
|
@ -227,7 +230,7 @@ export const APITokenManager: Component<APITokenManagerProps> = (props) => {
|
||||||
|
|
||||||
<div class="bg-yellow-50 dark:bg-yellow-900/30 border border-yellow-300 dark:border-yellow-700 rounded-lg p-3">
|
<div class="bg-yellow-50 dark:bg-yellow-900/30 border border-yellow-300 dark:border-yellow-700 rounded-lg p-3">
|
||||||
<p class="text-sm text-yellow-900 dark:text-yellow-100 font-medium">
|
<p class="text-sm text-yellow-900 dark:text-yellow-100 font-medium">
|
||||||
💡 Next: Use this token on the <a href="/settings/docker-agents" class="underline hover:no-underline font-bold">Docker Agents</a> page to deploy monitoring.
|
💡 Keep this token safe and use it anywhere Pulse requires API authentication—Docker agents, automations, or custom integrations.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3574,50 +3574,49 @@ const Settings: Component<SettingsProps> = (props) => {
|
||||||
{/* Security setup now handled by first-run wizard */}
|
{/* Security setup now handled by first-run wizard */}
|
||||||
|
|
||||||
{/* API Token - Show always to allow API access even when auth is disabled */}
|
{/* API Token - Show always to allow API access even when auth is disabled */}
|
||||||
<Show when={!securityStatusLoading()}>
|
<Card
|
||||||
<Card
|
padding="none"
|
||||||
padding="none"
|
class="overflow-hidden border border-gray-200 dark:border-gray-700"
|
||||||
class="overflow-hidden border border-gray-200 dark:border-gray-700"
|
border={false}
|
||||||
border={false}
|
>
|
||||||
>
|
{/* Header */}
|
||||||
{/* Header */}
|
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||||
<div class="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
<div class="flex items-center gap-3">
|
||||||
<div class="flex items-center gap-3">
|
<div class="p-2 bg-blue-100 dark:bg-blue-900/50 rounded-lg">
|
||||||
<div class="p-2 bg-blue-100 dark:bg-blue-900/50 rounded-lg">
|
<svg
|
||||||
<svg
|
class="w-5 h-5 text-blue-600 dark:text-blue-400"
|
||||||
class="w-5 h-5 text-blue-600 dark:text-blue-400"
|
fill="none"
|
||||||
fill="none"
|
viewBox="0 0 24 24"
|
||||||
viewBox="0 0 24 24"
|
stroke="currentColor"
|
||||||
stroke="currentColor"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
stroke-linecap="round"
|
||||||
stroke-linecap="round"
|
stroke-linejoin="round"
|
||||||
stroke-linejoin="round"
|
stroke-width="2"
|
||||||
stroke-width="2"
|
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"
|
||||||
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<SectionHeader
|
|
||||||
title="API token"
|
|
||||||
description="For automation and integrations"
|
|
||||||
size="sm"
|
|
||||||
class="flex-1"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<SectionHeader
|
||||||
|
title="API token"
|
||||||
{/* Content */}
|
description="For automation and integrations"
|
||||||
<div class="p-6">
|
size="sm"
|
||||||
<APITokenManager
|
class="flex-1"
|
||||||
currentTokenHint={securityStatus()?.apiTokenHint}
|
|
||||||
onTokensChanged={() => {
|
|
||||||
void loadSecurityStatus();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
</Show>
|
|
||||||
|
{/* Content */}
|
||||||
|
<div class="p-6">
|
||||||
|
<APITokenManager
|
||||||
|
currentTokenHint={securityStatus()?.apiTokenHint}
|
||||||
|
onTokensChanged={() => {
|
||||||
|
void loadSecurityStatus();
|
||||||
|
}}
|
||||||
|
refreshing={securityStatusLoading()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
{/* Advanced - Only show if auth is enabled */}
|
{/* Advanced - Only show if auth is enabled */}
|
||||||
{/* Advanced Options section removed - was only used for Registration Tokens */}
|
{/* Advanced Options section removed - was only used for Registration Tokens */}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue