feat: enhance VPS management interface with search, filter, and toast notifications
All checks were successful
Build and Push Docker Images / docker (push) Successful in 42s

- Added a DashboardToolbar component for searching, filtering, and sorting VPS instances.
- Implemented a ConfirmDialog component for destructive actions confirmation.
- Introduced a Modal component for displaying modals with focus management.
- Created a Toast component for displaying notifications with different types (success, error, info).
- Refactored VpsCard to utilize new Metric component for displaying CPU and RAM usage.
- Improved user experience with local storage for collapsed state in VpsCard.
- Added clipboard utility functions for copying text and downloading content.
- Enhanced CSS styles for better dark mode support and animations.
- Updated various UI controls for consistency and improved accessibility.
This commit is contained in:
jeanotx32
2026-08-01 01:22:45 -04:00
parent 022fbabe5d
commit f37b639226
20 changed files with 1689 additions and 727 deletions

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import { Monitor, Fingerprint } from 'lucide-react'
import { login, register, loginWithPasskey } from '../api/client'
import { inputClass, PasswordInput } from './ui/controls'
export default function LoginPage({ isFirstUser, passkeyEnabled, onAuthenticated }) {
const [username, setUsername] = useState('')
@@ -90,47 +91,46 @@ export default function LoginPage({ isFirstUser, passkeyEnabled, onAuthenticated
)}
{error && (
<div className="bg-red-950/40 border border-red-800/50 rounded-lg px-3 py-2 text-xs text-red-300">
<div role="alert" className="bg-red-950/40 border border-red-800/50 rounded-lg px-3 py-2 text-xs text-red-300">
{error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-xs text-gray-400 mb-1.5">Nom d'utilisateur</label>
<label htmlFor="login-username" className="block text-xs text-gray-400 mb-1.5">Nom d'utilisateur</label>
<input
id="login-username"
type="text"
required
autoFocus
autoComplete="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-indigo-500 transition-colors"
className={inputClass}
/>
</div>
<div>
<label className="block text-xs text-gray-400 mb-1.5">Mot de passe</label>
<input
type="password"
<label htmlFor="login-password" className="block text-xs text-gray-400 mb-1.5">Mot de passe</label>
<PasswordInput
id="login-password"
required
autoComplete={isRegister ? 'new-password' : 'current-password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-indigo-500 transition-colors"
/>
</div>
{isRegister && (
<div>
<label className="block text-xs text-gray-400 mb-1.5">Confirmer le mot de passe</label>
<input
type="password"
<label htmlFor="login-password2" className="block text-xs text-gray-400 mb-1.5">Confirmer le mot de passe</label>
<PasswordInput
id="login-password2"
required
autoComplete="new-password"
value={password2}
onChange={(e) => setPassword2(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-indigo-500 transition-colors"
/>
</div>
)}