fix: UI overhaul — align all HTMX views with Lumina Design System (46 fixes)
- All 7 pages rewritten: Dashboard, Terminal, Snippets, Keychain, SFTP, Settings, Brief - Apply consistent Lumina glassmorphic theme (rounded-2xl, shadows, borders, tracking) - Fix SVG icons not rendering — return template.HTML instead of string - Fix Alpine x-data scoping: terminal tabs, SFTP modals, snippet tag buttons - Fix dashboard search sending literal ?q=... - Rebuild CSS with all new Tailwind classes (62KB) - Add .air.toml with hot-reload config, docs/PERBAIKANUI.md checklist - Remove unused backup partials
This commit is contained in:
+94
-58
@@ -19,102 +19,138 @@
|
||||
<h1 class="text-lg font-semibold text-on-surface">{{.Title}}</h1>
|
||||
</header>
|
||||
<main id="main-content" class="flex-1 overflow-auto p-6">
|
||||
<div id="sftp-page" class="max-w-7xl mx-auto"
|
||||
<div id="sftp-page" class="max-w-7xl mx-auto space-y-6 relative"
|
||||
x-data="{ toast: '', showToast: false, showFolderModal: false, folderPane: 'local' }"
|
||||
x-init="$watch('showToast', v => v && setTimeout(() => showToast = false, 3500))">
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<!-- Local pane -->
|
||||
<div class="rounded-xl bg-white/70 backdrop-blur-md border border-outline-variant/30 overflow-hidden">
|
||||
<div class="flex items-center gap-2 px-4 py-3 border-b border-outline-variant/20 bg-white/50">
|
||||
<svg class="w-4 h-4 text-on-surface-variant" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/></svg>
|
||||
<span class="text-sm font-medium text-on-surface">Local Workstation</span>
|
||||
<div class="flex-1"></div>
|
||||
<button @click="folderPane = 'local'; showFolderModal = true"
|
||||
class="p-1 rounded-lg hover:bg-surface-container-low text-on-surface-variant hover:text-on-surface transition-colors" title="New Folder">
|
||||
<!-- Toast -->
|
||||
<div x-show="showToast" x-cloak
|
||||
class="fixed bottom-6 right-6 z-[180] bg-inverse-surface text-inverse-on-surface px-5 py-3.5 rounded-xl shadow-xl flex items-center gap-3 border border-outline/20 animate-slide-in text-xs font-semibold">
|
||||
<svg class="w-4 h-4 text-secondary animate-bounce" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
||||
<span x-text="toast"></span>
|
||||
</div>
|
||||
|
||||
<!-- Header Panel -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-outline-variant/30 pb-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold text-on-surface tracking-tight">SFTP Secure File Sync</h2>
|
||||
<p class="text-xs text-on-surface-variant mt-0.5">Dual streams for real-time asset transmission and file editing</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button hx-get="/sftp" hx-target="#main-content"
|
||||
class="flex items-center gap-2 px-3 py-1.5 rounded-lg border border-outline-variant/30 hover:border-primary text-xs font-semibold text-on-surface bg-white transition-all hover:bg-surface-container">
|
||||
<svg class="w-3.5 h-3.5" 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>
|
||||
Reset State
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dual Panel Body -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
|
||||
<!-- Local Pane -->
|
||||
<div class="bg-white rounded-2xl border border-outline-variant/30 overflow-hidden shadow-sm flex flex-col h-[520px]">
|
||||
<div class="p-4 border-b border-outline-variant/50 bg-surface-container-low flex items-center justify-between">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<svg class="w-4 h-4 text-outline shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/></svg>
|
||||
<span class="font-bold text-xs text-on-surface truncate uppercase tracking-wider">Local Workstation</span>
|
||||
</div>
|
||||
<button @click="$dispatch('open-folder-modal', { pane: 'local' })"
|
||||
class="text-primary hover:text-primary/80 p-1 rounded hover:bg-primary/5 transition-all" title="New Local Folder">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m-5-8H5a2 2 0 00-2 2v8a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2v2"/></svg>
|
||||
</button>
|
||||
<input type="search" placeholder="Filter files..." hx-get="/sftp/pane?pane=local&q=..." hx-trigger="keyup delay:200ms" hx-target="#local-pane"
|
||||
class="w-40 px-2 py-1 rounded-lg border border-outline-variant/30 bg-white text-xs text-on-surface placeholder-on-surface-variant focus:outline-none focus:ring-2 focus:ring-primary/30">
|
||||
</div>
|
||||
<div class="flex items-center gap-1 px-4 py-2 border-b border-outline-variant/10 text-xs text-on-surface-variant font-mono bg-surface-container-low/30">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<div class="px-4 py-2 border-b border-outline-variant/20 bg-surface-container-lowest flex items-center gap-1.5 text-xs text-outline font-mono overflow-x-auto">
|
||||
{{range .LocalBreadcrumb}}
|
||||
<a href="#" hx-get="/sftp/pane?pane=local&dir={{.Path}}" hx-target="#local-pane"
|
||||
class="hover:text-primary transition-colors">{{.Name}}</a>
|
||||
<span class="text-on-surface-variant/40">/</span>
|
||||
class="text-primary hover:underline cursor-pointer">{{.Name}}</a>
|
||||
<svg class="w-3 h-3 text-outline/40 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="local-pane">
|
||||
|
||||
<!-- Search -->
|
||||
<div class="p-3 border-b border-outline-variant/10 relative">
|
||||
<svg class="absolute left-6 top-1/2 -translate-y-1/2 text-outline w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
|
||||
<input type="search" name="q" placeholder="Filter local files..."
|
||||
hx-get="/sftp/pane?pane=local" hx-trigger="keyup delay:200ms" hx-target="#local-pane"
|
||||
class="w-full bg-surface-container-low pl-10 pr-4 py-1.5 border border-outline-variant/30 rounded-lg text-xs font-medium focus:outline-none focus:border-primary focus:bg-white">
|
||||
</div>
|
||||
|
||||
<div id="local-pane" class="flex-1 overflow-y-auto min-h-0">
|
||||
{{template "sftp_pane" dict "Pane" "local" "Files" .LocalFiles "Breadcrumb" .LocalBreadcrumb}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Remote pane -->
|
||||
<div class="rounded-xl bg-white/70 backdrop-blur-md border border-outline-variant/30 overflow-hidden">
|
||||
<div class="flex items-center gap-2 px-4 py-3 border-b border-outline-variant/20 bg-white/50">
|
||||
<svg class="w-4 h-4 text-on-surface-variant" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/></svg>
|
||||
<span class="text-sm font-medium text-on-surface">Remote Cluster Node</span>
|
||||
<div class="flex-1"></div>
|
||||
<button @click="folderPane = 'remote'; showFolderModal = true"
|
||||
class="p-1 rounded-lg hover:bg-surface-container-low text-on-surface-variant hover:text-on-surface transition-colors" title="New Folder">
|
||||
<!-- Remote Pane -->
|
||||
<div class="bg-white rounded-2xl border border-outline-variant/30 overflow-hidden shadow-sm flex flex-col h-[520px]">
|
||||
<div class="p-4 border-b border-outline-variant/50 bg-surface-container-low flex items-center justify-between">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<svg class="w-4 h-4 text-primary shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"/></svg>
|
||||
<span class="font-bold text-xs text-on-surface truncate uppercase tracking-wider">Remote Cluster Node</span>
|
||||
</div>
|
||||
<button @click="$dispatch('open-folder-modal', { pane: 'remote' })"
|
||||
class="text-primary hover:text-primary/80 p-1 rounded hover:bg-primary/5 transition-all" title="New Remote Folder">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m-5-8H5a2 2 0 00-2 2v8a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2v2"/></svg>
|
||||
</button>
|
||||
<input type="search" placeholder="Filter files..." hx-get="/sftp/pane?pane=remote&q=..." hx-trigger="keyup delay:200ms" hx-target="#remote-pane"
|
||||
class="w-40 px-2 py-1 rounded-lg border border-outline-variant/30 bg-white text-xs text-on-surface placeholder-on-surface-variant focus:outline-none focus:ring-2 focus:ring-primary/30">
|
||||
</div>
|
||||
<div class="flex items-center gap-1 px-4 py-2 border-b border-outline-variant/10 text-xs text-on-surface-variant font-mono bg-surface-container-low/30">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<div class="px-4 py-2 border-b border-outline-variant/20 bg-surface-container-lowest flex items-center gap-1.5 text-xs text-outline font-mono overflow-x-auto">
|
||||
{{range .RemoteBreadcrumb}}
|
||||
<a href="#" hx-get="/sftp/pane?pane=remote&dir={{.Path}}" hx-target="#remote-pane"
|
||||
class="hover:text-primary transition-colors">{{.Name}}</a>
|
||||
<span class="text-on-surface-variant/40">/</span>
|
||||
class="text-primary hover:underline cursor-pointer">{{.Name}}</a>
|
||||
<svg class="w-3 h-3 text-outline/40 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
|
||||
{{end}}
|
||||
</div>
|
||||
<div id="remote-pane">
|
||||
|
||||
<!-- Search -->
|
||||
<div class="p-3 border-b border-outline-variant/10 relative">
|
||||
<svg class="absolute left-6 top-1/2 -translate-y-1/2 text-outline w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
|
||||
<input type="search" name="q" placeholder="Filter remote files..."
|
||||
hx-get="/sftp/pane?pane=remote" hx-trigger="keyup delay:200ms" hx-target="#remote-pane"
|
||||
class="w-full bg-surface-container-low pl-10 pr-4 py-1.5 border border-outline-variant/30 rounded-lg text-xs font-medium focus:outline-none focus:border-primary focus:bg-white">
|
||||
</div>
|
||||
|
||||
<div id="remote-pane" class="flex-1 overflow-y-auto min-h-0">
|
||||
{{template "sftp_pane" dict "Pane" "remote" "Files" .RemoteFiles "Breadcrumb" .RemoteBreadcrumb}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drop zone -->
|
||||
<div class="mt-6 rounded-xl border-2 border-dashed border-outline-variant/30 p-8 flex flex-col items-center justify-center text-on-surface-variant/50 hover:border-primary/30 hover:text-primary/50 transition-colors cursor-pointer">
|
||||
<svg class="w-10 h-10 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/></svg>
|
||||
<p class="text-sm font-medium">Drop files here to upload</p>
|
||||
<p class="text-xs mt-1">or click to browse</p>
|
||||
<!-- Drop Zone -->
|
||||
<div class="p-8 border-2 border-dashed border-outline-variant/40 rounded-2xl text-center bg-white/40 flex flex-col items-center justify-center space-y-2 relative group hover:border-primary transition-colors cursor-pointer">
|
||||
<svg class="w-10 h-10 text-outline group-hover:text-primary group-hover:scale-110 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"/></svg>
|
||||
<h3 class="font-bold text-sm text-on-surface">Drag files directly here to start batch upload</h3>
|
||||
<p class="text-xs text-on-surface-variant max-w-sm">
|
||||
Files are automatically split into parallel streams of 5MB chunks and secured with AES-256 keychain keys.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Toast -->
|
||||
<div x-show="showToast" x-cloak
|
||||
x-text="toast"
|
||||
class="fixed bottom-6 right-6 bg-[#191c1e] text-white px-4 py-3 rounded-xl text-sm shadow-lg z-50 animate-slide-in"
|
||||
style="max-width: 360px;">
|
||||
</div>
|
||||
|
||||
<!-- Folder create modal -->
|
||||
<!-- Folder Modal -->
|
||||
<div id="folder-modal" class="fixed inset-0 z-50" x-data="{ open: false }" x-show="open" x-cloak
|
||||
@open-folder-modal.window="open = true; folderPane = $event.detail.pane" @keydown.escape.window="open = false">
|
||||
<div class="absolute inset-0 bg-black/30 backdrop-blur-sm" @click="open = false"></div>
|
||||
<div class="absolute inset-0 bg-black/40 backdrop-blur-sm" @click="open = false"></div>
|
||||
<div class="absolute inset-0 flex items-center justify-center p-4" x-show="open" x-transition>
|
||||
<div class="bg-white rounded-xl shadow-xl w-full max-w-sm p-6" @click.outside="open = false">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-lg font-semibold text-on-surface">New Folder</h2>
|
||||
<button @click="open = false" class="p-1 rounded-lg hover:bg-surface-container-low text-on-surface-variant">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
<div class="bg-white rounded-2xl max-w-sm w-full border border-outline-variant/30 shadow-2xl p-6 space-y-4">
|
||||
<div class="flex justify-between items-center border-b border-outline-variant/30 pb-3">
|
||||
<h3 class="font-bold text-lg text-on-surface">New Folder</h3>
|
||||
<button @click="open = false" class="text-outline hover:text-primary p-1 rounded">Cancel</button>
|
||||
</div>
|
||||
<form hx-post="/sftp/mkdir" hx-target="#local-pane" hx-swap="outerHTML"
|
||||
@submit="open = false"
|
||||
class="space-y-4">
|
||||
@submit="open = false" class="space-y-4 text-sm">
|
||||
<input type="hidden" name="pane" :value="folderPane">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-on-surface mb-1">Folder Name</label>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-xs font-bold text-on-surface-variant">FOLDER NAME</label>
|
||||
<input type="text" name="name" required placeholder="e.g. new-project"
|
||||
class="w-full px-3 py-2 rounded-lg border border-outline-variant/50 bg-white text-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/30">
|
||||
class="w-full bg-surface-container-low border border-outline-variant/40 rounded-lg py-2.5 px-4 focus:ring-2 focus:ring-primary/20 focus:border-primary focus:outline-none transition-all font-medium">
|
||||
</div>
|
||||
<div class="flex justify-end gap-3 pt-2">
|
||||
<div class="pt-4 flex justify-end gap-3 border-t border-outline-variant/20">
|
||||
<button type="button" @click="open = false"
|
||||
class="px-4 py-2 rounded-lg text-sm font-medium text-on-surface-variant hover:bg-surface-container-low transition-colors">Cancel</button>
|
||||
class="px-4 py-2 text-xs font-bold text-outline hover:text-on-surface">Cancel</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 rounded-lg text-sm font-medium bg-primary text-white hover:bg-primary/90 transition-colors">Create</button>
|
||||
class="bg-primary text-white py-2 px-5 rounded-lg font-bold text-xs shadow-md hover:bg-primary/90 transition-all">Create Folder</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user