Files
HostKeeper/app/backend/views/sftp_content.html
T
swanadiva e70b133073 fix: add missing _content partial templates for HTMX nav (terminal, sftp, settings, brief)
- Create terminal_content.html, sftp_content.html, settings_content.html, brief_content.html
- Update full page templates to use partial templates via {{template}}
- Handlers already rendered these templates on HX-Request, but files were missing → 500
- All pages now return 200 for both full page and HTMX nav requests
2026-07-07 15:36:12 +07:00

141 lines
10 KiB
HTML

{{define "sftp_content"}}
<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))"
@toast-message.window="toast = $event.detail.message; showToast = true">
<!-- 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>
</div>
<!-- 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="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>
<!-- 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="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>
</div>
<!-- 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="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>
<!-- 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="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>
<!-- 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/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-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 text-sm">
<input type="hidden" name="pane" :value="folderPane">
<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 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="pt-4 flex justify-end gap-3 border-t border-outline-variant/20">
<button type="button" @click="open = false"
class="px-4 py-2 text-xs font-bold text-outline hover:text-on-surface">Cancel</button>
<button type="submit"
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>
</div>
</div>
</div>
{{end}}