mirror of
https://github.com/kossakovsky/n8n-install.git
synced 2026-03-07 14:23:08 +00:00
feat: improve welcome page ux and remove beta/stable switch commands
- add click-to-toggle password visibility (replaces hold-to-reveal) - add copy button for username fields - add "keeping up to date" section with update commands - remove switch-beta/switch-stable from commands list and docs
This commit is contained in:
@@ -38,9 +38,6 @@ make logs s=<service> # View logs for specific service
|
||||
make status # Show container status
|
||||
make monitor # Live CPU/memory monitoring
|
||||
make restarts # Show restart count per container
|
||||
|
||||
make switch-beta # Switch to beta (develop branch)
|
||||
make switch-stable # Switch to stable (main branch)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -238,9 +238,7 @@
|
||||
{ cmd: 'make doctor', desc: 'Run system diagnostics' },
|
||||
{ cmd: 'make update', desc: 'Update system and services' },
|
||||
{ cmd: 'make update-preview', desc: 'Preview available updates' },
|
||||
{ cmd: 'make clean', desc: 'Remove unused Docker resources' },
|
||||
{ cmd: 'make switch-beta', desc: 'Switch to beta (develop branch)' },
|
||||
{ cmd: 'make switch-stable', desc: 'Switch to stable (main branch)' }
|
||||
{ cmd: 'make clean', desc: 'Remove unused Docker resources' }
|
||||
];
|
||||
|
||||
// DOM Elements
|
||||
@@ -251,6 +249,41 @@
|
||||
const errorToast = document.getElementById('error-toast');
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
|
||||
/**
|
||||
* Create a copy button for any text
|
||||
*/
|
||||
function createCopyButton(textToCopy) {
|
||||
const copyBtn = document.createElement('button');
|
||||
copyBtn.className = 'p-1.5 rounded-lg hover:bg-surface-400 transition-colors focus:outline-none focus:ring-2 focus:ring-brand/50';
|
||||
copyBtn.innerHTML = `
|
||||
<svg class="w-4 h-4 text-gray-500 hover:text-brand transition-colors copy-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<svg class="w-4 h-4 text-brand check-icon hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
`;
|
||||
copyBtn.title = 'Copy to clipboard';
|
||||
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
const copyIcon = copyBtn.querySelector('.copy-icon');
|
||||
const checkIcon = copyBtn.querySelector('.check-icon');
|
||||
copyIcon.classList.add('hidden');
|
||||
checkIcon.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
copyIcon.classList.remove('hidden');
|
||||
checkIcon.classList.add('hidden');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
}
|
||||
});
|
||||
|
||||
return copyBtn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error toast
|
||||
*/
|
||||
@@ -284,34 +317,34 @@
|
||||
const toggleBtn = document.createElement('button');
|
||||
toggleBtn.className = 'p-1.5 rounded-lg hover:bg-surface-400 transition-colors focus:outline-none focus:ring-2 focus:ring-brand/50';
|
||||
toggleBtn.innerHTML = `
|
||||
<svg class="w-4 h-4 text-gray-500 hover:text-brand transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-4 h-4 text-gray-500 hover:text-brand transition-colors eye-open" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
|
||||
</svg>
|
||||
<svg class="w-4 h-4 text-gray-500 hover:text-brand transition-colors eye-closed hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"/>
|
||||
</svg>
|
||||
`;
|
||||
toggleBtn.title = 'Hold to reveal';
|
||||
toggleBtn.title = 'Toggle visibility';
|
||||
|
||||
// Show password on mouse down, hide on mouse up/leave
|
||||
const showPassword = () => {
|
||||
passwordSpan.textContent = passwordSpan.dataset.password;
|
||||
passwordSpan.dataset.hidden = 'false';
|
||||
};
|
||||
const hidePassword = () => {
|
||||
passwordSpan.textContent = '*'.repeat(Math.min(password.length, 12));
|
||||
passwordSpan.dataset.hidden = 'true';
|
||||
};
|
||||
// Toggle password visibility on click
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
const isHidden = passwordSpan.dataset.hidden === 'true';
|
||||
const eyeOpen = toggleBtn.querySelector('.eye-open');
|
||||
const eyeClosed = toggleBtn.querySelector('.eye-closed');
|
||||
|
||||
toggleBtn.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
showPassword();
|
||||
if (isHidden) {
|
||||
passwordSpan.textContent = passwordSpan.dataset.password;
|
||||
passwordSpan.dataset.hidden = 'false';
|
||||
eyeOpen.classList.add('hidden');
|
||||
eyeClosed.classList.remove('hidden');
|
||||
} else {
|
||||
passwordSpan.textContent = '*'.repeat(Math.min(password.length, 12));
|
||||
passwordSpan.dataset.hidden = 'true';
|
||||
eyeOpen.classList.remove('hidden');
|
||||
eyeClosed.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
toggleBtn.addEventListener('mouseup', hidePassword);
|
||||
toggleBtn.addEventListener('mouseleave', hidePassword);
|
||||
toggleBtn.addEventListener('touchstart', (e) => {
|
||||
e.preventDefault();
|
||||
showPassword();
|
||||
});
|
||||
toggleBtn.addEventListener('touchend', hidePassword);
|
||||
|
||||
// Copy button
|
||||
const copyBtn = document.createElement('button');
|
||||
@@ -380,9 +413,11 @@
|
||||
let fields = [];
|
||||
if (creds.username) {
|
||||
fields.push(`
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex justify-between items-center" id="user-${key}">
|
||||
<span class="text-gray-500 text-sm">Username:</span>
|
||||
<span class="font-mono text-sm select-all text-gray-300">${escapeHtml(creds.username)}</span>
|
||||
<div class="flex items-center gap-1">
|
||||
<span class="font-mono text-sm select-all text-gray-300">${escapeHtml(creds.username)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
@@ -458,11 +493,17 @@
|
||||
${credentialsHtml}
|
||||
`;
|
||||
|
||||
// Add password fields after card is created
|
||||
// Add password fields and copy buttons after card is created
|
||||
if (serviceData.credentials) {
|
||||
const creds = serviceData.credentials;
|
||||
|
||||
setTimeout(() => {
|
||||
if (creds.username) {
|
||||
const userContainer = card.querySelector(`#user-${key} .flex.items-center`);
|
||||
if (userContainer) {
|
||||
userContainer.appendChild(createCopyButton(creds.username));
|
||||
}
|
||||
}
|
||||
if (creds.password) {
|
||||
const pwdContainer = card.querySelector(`#pwd-${key}`);
|
||||
if (pwdContainer) {
|
||||
|
||||
@@ -122,6 +122,31 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Update Section -->
|
||||
<section class="mb-16">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div class="w-10 h-10 rounded-lg bg-brand/10 border border-brand/20 flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-brand" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="text-2xl font-semibold text-white">Keeping Up to Date</h2>
|
||||
</div>
|
||||
<div class="bg-surface-100 rounded-xl border border-surface-400 p-6">
|
||||
<p class="text-gray-300 mb-4">Keep your services up to date with the latest features and security patches:</p>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-start gap-3">
|
||||
<code class="text-brand font-mono text-sm bg-surface-200 px-2 py-1 rounded">make update-preview</code>
|
||||
<span class="text-gray-400 text-sm">Preview available updates before applying</span>
|
||||
</div>
|
||||
<div class="flex items-start gap-3">
|
||||
<code class="text-brand font-mono text-sm bg-surface-200 px-2 py-1 rounded">make update</code>
|
||||
<span class="text-gray-400 text-sm">Update all services to the latest versions</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Commands Section -->
|
||||
<section class="mb-16">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
|
||||
Reference in New Issue
Block a user