feat: comprehensive UI polish with responsive design, animations, and modern styling

- Rewrite globals.css with professional utility classes (btn-primary/secondary/danger/ghost, input-field, card/card-hover, badge system, stat-card, skeleton, page-header, animations)
- Responsive dashboard layout with mobile hamburger menu, slide-out sidebar, backdrop overlay
- Polish login/register pages with gradient backgrounds, slide-up animations, loading spinners
- Dashboard home with stat cards grid (2-col mobile, 4-col desktop), skeleton loading, empty states
- Apps list with desktop table + mobile card views, proper badges
- Deploy wizard with responsive step indicator (checkmarks, progress line), mobile-friendly forms
- App detail page with responsive header/action buttons, improved logs tabs, resource scaling
- Admin users page with desktop table + mobile cards, skeleton loading
- Admin clusters page with responsive layout, animated resource panel
- Admin pools page with responsive form grids, improved pool cards
- Consistent rounded-2xl cards, rounded-xl buttons/inputs, active:scale feedback throughout
This commit is contained in:
keyhan
2026-04-05 18:34:27 +03:30
parent e7d70f87cd
commit c8e66f6771
12 changed files with 822 additions and 462 deletions
+44 -32
View File
@@ -140,25 +140,37 @@ export default function DeployPage() {
};
return (
<div className="max-w-2xl mx-auto space-y-8">
<div className="max-w-2xl mx-auto space-y-8 animate-fade-in">
<div>
<h1 className="text-2xl font-bold text-gray-900">Deploy New Application</h1>
<p className="mt-1 text-gray-500">Follow the steps to deploy your app to the cloud.</p>
<h1 className="page-title">Deploy New Application</h1>
<p className="page-subtitle">Follow the steps to deploy your app to the cloud.</p>
</div>
{/* Step indicator */}
<div className="flex items-center space-x-2">
<div className="flex items-center justify-between">
{steps.map((label, i) => (
<div key={label} className="flex items-center">
<div className={`flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium ${
i <= step ? 'bg-primary-600 text-white' : 'bg-gray-200 text-gray-500'
}`}>
{i + 1}
<div key={label} className="flex items-center flex-1 last:flex-none">
<div className="flex flex-col items-center">
<div className={`flex items-center justify-center w-9 h-9 rounded-full text-sm font-bold transition-all duration-300 ${
i < step
? 'bg-emerald-500 text-white shadow-md'
: i === step
? 'bg-primary-600 text-white shadow-lg ring-4 ring-primary-100'
: 'bg-gray-200 text-gray-500'
}`}>
{i < step ? '✓' : i + 1}
</div>
<span className={`mt-1.5 text-xs font-medium hidden sm:block ${
i <= step ? 'text-gray-900' : 'text-gray-400'
}`}>
{label}
</span>
</div>
<span className={`ml-2 text-sm ${i <= step ? 'text-gray-900 font-medium' : 'text-gray-400'}`}>
{label}
</span>
{i < steps.length - 1 && <div className="w-8 h-0.5 bg-gray-200 mx-3" />}
{i < steps.length - 1 && (
<div className={`flex-1 h-0.5 mx-2 sm:mx-3 rounded-full transition-colors duration-300 ${
i < step ? 'bg-emerald-400' : 'bg-gray-200'
}`} />
)}
</div>
))}
</div>
@@ -166,8 +178,8 @@ export default function DeployPage() {
<div className="card">
{/* Step 0: Basic Info */}
{step === 0 && (
<div className="space-y-4">
<h2 className="text-lg font-semibold">Basic Information</h2>
<div className="space-y-5">
<h2 className="text-lg font-semibold text-gray-900">Basic Information</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Application Name</label>
<input
@@ -326,10 +338,10 @@ export default function DeployPage() {
{/* Step 1: Runtime & Database */}
{step === 1 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold">Runtime & Database</h2>
<h2 className="text-lg font-semibold text-gray-900">Runtime & Database</h2>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">Application Runtime</label>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{[
{ value: 'nodejs', label: 'Node.js', icon: '🟩', desc: 'Express, NestJS, Fastify...' },
{ value: 'laravel', label: 'Laravel', icon: '🟧', desc: 'PHP 8.3, Composer, Artisan' },
@@ -351,7 +363,7 @@ export default function DeployPage() {
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-3">Database</label>
<div className="grid grid-cols-3 gap-4">
<div className="grid grid-cols-3 gap-3 sm:gap-4">
{[
{ value: 'none', label: 'None', icon: '❌' },
{ value: 'postgresql', label: 'PostgreSQL', icon: '🐘' },
@@ -377,12 +389,12 @@ export default function DeployPage() {
{/* Step 2: Resources */}
{step === 2 && (
<div className="space-y-6">
<h2 className="text-lg font-semibold">Resources & Configuration</h2>
<h2 className="text-lg font-semibold text-gray-900">Resources & Configuration</h2>
{/* Cluster Assignment Mode */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Cluster Assignment</label>
<div className="grid grid-cols-3 gap-3 mb-4">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
<button
type="button"
onClick={() => {
@@ -542,7 +554,7 @@ export default function DeployPage() {
)}
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">CPU Request</label>
<select className="input-field" value={form.cpuRequest} onChange={(e) => setForm({ ...form, cpuRequest: e.target.value })}>
@@ -580,7 +592,7 @@ export default function DeployPage() {
</select>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Replicas</label>
<input
@@ -606,7 +618,7 @@ export default function DeployPage() {
{/* Environment Variables */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Environment Variables</label>
<div className="flex space-x-2 mb-3">
<div className="flex flex-col sm:flex-row gap-2 mb-3">
<input
className="input-field flex-1"
placeholder="KEY"
@@ -619,7 +631,7 @@ export default function DeployPage() {
value={envVal}
onChange={(e) => setEnvVal(e.target.value)}
/>
<button type="button" onClick={addEnvVar} className="btn-secondary">Add</button>
<button type="button" onClick={addEnvVar} className="btn-secondary shrink-0">Add</button>
</div>
{Object.entries(form.envVars || {}).map(([key, value]) => (
<div key={key} className="flex items-center justify-between bg-gray-50 rounded-lg px-3 py-2 mb-2">
@@ -636,8 +648,8 @@ export default function DeployPage() {
{/* Step 3: Review */}
{step === 3 && (
<div className="space-y-4">
<h2 className="text-lg font-semibold">Review & Deploy</h2>
<div className="bg-gray-50 rounded-xl p-6 space-y-3">
<h2 className="text-lg font-semibold text-gray-900">Review & Deploy</h2>
<div className="bg-gray-50 rounded-xl p-5 sm:p-6 space-y-3">
<div className="flex justify-between">
<span className="text-sm text-gray-500">Name</span>
<span className="text-sm font-medium">{form.name}</span>
@@ -709,11 +721,11 @@ export default function DeployPage() {
)}
{/* Navigation */}
<div className="flex justify-between mt-8 pt-6 border-t border-gray-200">
<div className="flex justify-between mt-8 pt-6 border-t border-gray-100">
<button
onClick={() => setStep(step - 1)}
disabled={step === 0}
className="btn-secondary disabled:opacity-30"
className="btn-ghost disabled:opacity-0 disabled:pointer-events-none"
>
Back
</button>
@@ -721,7 +733,7 @@ export default function DeployPage() {
<button
onClick={() => setStep(step + 1)}
disabled={!canNext()}
className="btn-primary"
className="btn-primary disabled:opacity-50"
>
Next
</button>
@@ -729,12 +741,12 @@ export default function DeployPage() {
<button
onClick={handleSubmit}
disabled={createMutation.isPending}
className="btn-primary"
className="btn-primary disabled:opacity-50"
>
{createMutation.isPending
? uploadProgress > 0 && uploadProgress < 100
? `Uploading... ${uploadProgress}%`
: 'Deploying...'
? `Uploading... ${uploadProgress}%`
: 'Deploying...'
: '🚀 Deploy Application'}
</button>
)}