feat(admin): redesign storage backend management UI
All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m31s

Implement a new card-based UI for managing storage backends in the admin panel. This update improves the visual presentation and usability of the storage configuration page.

Key changes:
- Added comprehensive CSS styles for storage cards, including status indicators, metadata layouts, and action buttons.
- Updated the storage admin template to render storage configurations as cards with type-specific details (Local, S3, SFTP, SMB, WebDAV).
- Integrated inline actions for testing, editing, disabling, and deleting storage backends.
- Enhanced sidebar link alignment with flexbox.
This commit is contained in:
2026-05-31 04:54:27 +03:00
parent 3423c141be
commit 0503fad9af
11 changed files with 637 additions and 181 deletions

View File

@@ -4,18 +4,20 @@
<section class="app-shell admin-shell" aria-labelledby="admin-storage-title">
<aside class="app-sidebar">
<nav class="sidebar-nav">
<a class="sidebar-link" href="/admin">Overview</a>
<a class="sidebar-link" href="/admin/files">Files</a>
<a class="sidebar-link" href="/admin/users">Users</a>
<a class="sidebar-link" href="/admin/settings">Settings</a>
<a class="sidebar-link is-active" href="/admin/storage">Storage</a>
<a class="sidebar-link" href="/admin">{{template "icon-dashboard" .}}<span>Overview</span></a>
<a class="sidebar-link" href="/admin/files">{{template "icon-folder" .}}<span>Files</span></a>
<a class="sidebar-link" href="/admin/users">{{template "icon-user-circle" .}}<span>Users</span></a>
<a class="sidebar-link" href="/admin/settings">{{template "icon-settings" .}}<span>Settings</span></a>
<a class="sidebar-link is-active" href="/admin/storage">{{template "icon-database" .}}<span>Storage</span></a>
</nav>
<hr class="sidebar-sep">
<nav class="sidebar-nav"><a class="sidebar-link" href="/app">My Files</a></nav>
<nav class="sidebar-nav">
<a class="sidebar-link" href="/app">{{template "icon-home-simple" .}}<span>My Files</span></a>
</nav>
<hr class="sidebar-sep">
<form class="sidebar-logout" action="/admin/logout" method="post">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<button class="button button-outline" type="submit">Sign out</button>
<button class="button button-outline" type="submit">{{template "icon-log-out" .}}<span>Sign out</span></button>
</form>
</aside>
@@ -24,126 +26,222 @@
<div>
<p class="kicker">Operator console</p>
<h1 id="admin-storage-title">{{.Data.PageTitle}}</h1>
<p class="muted-copy">Local storage is always active. Remote backends are proxied through Warpbox.</p>
</div>
</div>
{{if .Data.Error}}<p class="form-error">{{.Data.Error}}</p>{{end}}
<div class="card admin-table-card">
<div class="card-content">
<div class="table-header">
<div>
<h2>Storage backends</h2>
<p>Local storage is always available. Remote backends stay private behind Warpbox routes.</p>
</div>
</div>
<div class="admin-table-wrap">
<table class="admin-table">
<thead><tr><th>Name</th><th>Type</th><th>Status</th><th>Usage</th><th>Details</th><th>Actions</th></tr></thead>
<tbody>
{{range .Data.Storage}}
<tr>
<td>{{.Config.Name}}</td>
<td>{{if eq .Config.Provider "contabo"}}Contabo Object Storage{{else if eq .Config.Type "sftp"}}SFTP{{else if eq .Config.Type "smb"}}Samba{{else if eq .Config.Type "webdav"}}WebDAV{{else if eq .Config.Type "s3"}}S3 bucket{{else}}{{.Config.Type}}{{end}}</td>
<td>{{if .Config.Enabled}}Enabled{{else}}Disabled{{end}}</td>
<td>{{.UsageLabel}}</td>
<td>{{if eq .Config.Type "local"}}{{.Config.LocalPath}}{{else if eq .Config.Type "sftp"}}{{.Config.Username}}@{{.Config.Host}}:{{.Config.RemotePath}}{{else if eq .Config.Type "smb"}}{{.Config.Domain}}\{{.Config.Username}}@{{.Config.Host}}/{{.Config.Share}}:{{.Config.RemotePath}}{{else if eq .Config.Type "webdav"}}{{.Config.Endpoint}}/{{.Config.RemotePath}}{{else}}{{.Config.Bucket}} @ {{.Config.Endpoint}}{{end}}</td>
<td class="table-actions">
<form action="/admin/storage/{{.Config.ID}}/test" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-outline button-sm" type="submit">Test</button>
</form>
{{if ne .Config.ID "local"}}
<details class="row-edit">
<summary class="button button-outline button-sm">Edit</summary>
<form action="/admin/storage/{{.Config.ID}}/edit" method="post" class="row-edit-form storage-edit-form">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<label><span>Storage kind</span><select name="provider" data-storage-provider>
<option value="s3" {{if or (eq .Config.Provider "s3") (eq .Config.Provider "")}}selected{{end}}>S3 bucket</option>
<option value="contabo" {{if eq .Config.Provider "contabo"}}selected{{end}}>Contabo Object Storage</option>
<option value="sftp" {{if eq .Config.Provider "sftp"}}selected{{end}}>SFTP</option>
<option value="smb" {{if eq .Config.Provider "smb"}}selected{{end}}>Samba</option>
<option value="webdav" {{if eq .Config.Provider "webdav"}}selected{{end}}>WebDAV</option>
</select></label>
<label><span>Name</span><input name="name" value="{{.Config.Name}}" required></label>
<label data-provider-fields="s3 contabo"><span>Endpoint</span><input name="endpoint" value="{{.Config.Endpoint}}" required></label>
<label data-provider-fields="s3 contabo"><span>Region</span><input name="region" value="{{.Config.Region}}"></label>
<label data-provider-fields="s3 contabo"><span>Bucket / object storage name</span><input name="bucket" value="{{.Config.Bucket}}" required></label>
<label data-provider-fields="s3 contabo"><span>Access key</span><input name="access_key" value="{{.Config.AccessKey}}" required></label>
<label data-provider-fields="s3 contabo"><span>Secret key</span><input name="secret_key" type="password" placeholder="Leave unchanged"></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="use_ssl" {{if .Config.UseSSL}}checked{{end}}><span>Use TLS</span></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="path_style" {{if .Config.PathStyle}}checked{{end}}><span>Path-style lookup</span></label>
<label data-provider-fields="sftp smb"><span>Host</span><input name="host" value="{{.Config.Host}}" required></label>
<label data-provider-fields="sftp smb"><span>Port</span><input type="number" name="port" min="1" value="{{.Config.Port}}"></label>
<label data-provider-fields="smb"><span>Share</span><input name="share" value="{{.Config.Share}}" required></label>
<label data-provider-fields="smb"><span>Domain</span><input name="domain" value="{{.Config.Domain}}" placeholder="Optional"></label>
<label data-provider-fields="sftp smb webdav"><span>Username</span><input name="username" value="{{.Config.Username}}" required></label>
<label data-provider-fields="sftp smb webdav"><span>Password</span><input name="password" type="password" placeholder="Leave unchanged"></label>
<label data-provider-fields="sftp"><span>Private key</span><textarea name="private_key" rows="4" placeholder="Leave unchanged"></textarea></label>
<label data-provider-fields="sftp"><span>SSH host public key</span><textarea name="host_key" rows="3" placeholder="Optional, leave unchanged">{{.Config.HostKey}}</textarea></label>
<label data-provider-fields="webdav"><span>WebDAV URL</span><input name="endpoint" value="{{.Config.Endpoint}}" placeholder="https://files.example.com/remote.php/dav/files/user"></label>
<label data-provider-fields="sftp smb webdav"><span>Remote path</span><input name="remote_path" value="{{.Config.RemotePath}}" placeholder="/srv/warpbox"></label>
<button class="button button-primary button-sm" type="submit">Save</button>
</form>
</details>
<form action="/admin/storage/{{.Config.ID}}/disable" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-danger button-sm" type="submit" {{if .InUse}}disabled{{end}}>Disable</button>
</form>
<form action="/admin/storage/{{.Config.ID}}/delete" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-danger button-sm" type="submit" {{if .InUse}}disabled{{end}}>Delete</button>
</form>
{{end}}
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
</div>
<div class="storage-stack">
<div class="card admin-table-card">
<div class="card-content">
<div class="table-header">
<div>
<h2>Add storage</h2>
<p>Choose a provider kind first. SFTP is useful for a server or NAS you control.</p>
{{range .Data.Storage}}
<div class="storage-card {{if eq .Config.ID "local"}}is-local{{end}}" data-storage-id="{{.Config.ID}}">
<div class="storage-card-header">
<div class="storage-card-identity">
<div class="storage-card-icon">
{{if eq .Config.Type "local"}}{{template "icon-hard-drive" $}}
{{else if eq .Config.Type "sftp"}}{{template "icon-database" $}}
{{else if eq .Config.Type "smb"}}{{template "icon-folder" $}}
{{else if eq .Config.Type "webdav"}}{{template "icon-cloud-sync" $}}
{{else}}{{template "icon-cloud-upload" $}}{{end}}
</div>
<div>
<strong class="storage-card-name">{{.Config.Name}}</strong>
<div class="storage-card-meta">
<span class="badge">{{if eq .Config.Provider "contabo"}}Contabo{{else if eq .Config.Type "sftp"}}SFTP{{else if eq .Config.Type "smb"}}Samba{{else if eq .Config.Type "webdav"}}WebDAV{{else if eq .Config.Type "s3"}}S3{{else if eq .Config.Type "local"}}Local files{{else}}{{.Config.Type}}{{end}}</span>
{{if eq .Config.ID "local"}}<span class="badge">Required</span>
{{else if .Config.Enabled}}<span class="badge badge-active">Enabled</span>
{{else}}<span class="badge badge-disabled">Disabled</span>{{end}}
{{if .UsageLabel}}<span class="storage-card-usage">{{.UsageLabel}}</span>{{end}}
</div>
</div>
</div>
<div class="storage-card-actions">
<form action="/admin/storage/{{.Config.ID}}/test" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-outline button-sm" type="submit">Test</button>
</form>
{{if ne .Config.ID "local"}}
<button class="button button-outline button-sm storage-edit-trigger" type="button">Edit</button>
{{if .Config.Enabled}}
<form action="/admin/storage/{{.Config.ID}}/disable" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-outline button-sm" type="submit" {{if .InUse}}disabled title="Backend is in use"{{end}}>Disable</button>
</form>
{{end}}
<form action="/admin/storage/{{.Config.ID}}/delete" method="post">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<button class="button button-danger button-sm" type="submit" {{if .InUse}}disabled title="Backend is in use"{{end}}>Delete</button>
</form>
{{end}}
</div>
</div>
<form class="settings-form" action="/admin/storage/s3" method="post">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<div class="settings-section">
<label><span>Storage kind</span><select name="provider" data-storage-provider>
<option value="s3">S3 bucket</option>
<option value="contabo">Contabo Object Storage</option>
<option value="sftp">SFTP</option>
<option value="smb">Samba</option>
<option value="webdav">WebDAV</option>
</select></label>
<label><span>Name</span><input name="name" placeholder="Bob's bucket" required></label>
<label data-provider-fields="s3 contabo"><span>Endpoint</span><input name="endpoint" placeholder="s3.example.com" required></label>
<label data-provider-fields="s3 contabo"><span>Region</span><input name="region" placeholder="us-east-1"></label>
<label data-provider-fields="s3 contabo"><span>Bucket / object storage name</span><input name="bucket" placeholder="My Main Bucket" required></label>
<label data-provider-fields="s3 contabo"><span>Access key</span><input name="access_key" required></label>
<label data-provider-fields="s3 contabo"><span>Secret key</span><input name="secret_key" type="password" required></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="use_ssl" checked><span>Use TLS</span></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="path_style"><span>Use path-style bucket lookup</span></label>
<label data-provider-fields="sftp smb"><span>Host</span><input name="host" placeholder="files.example.com" required></label>
<label data-provider-fields="sftp smb"><span>Port</span><input type="number" name="port" min="1"></label>
<label data-provider-fields="smb"><span>Share</span><input name="share" placeholder="uploads" required></label>
<label data-provider-fields="smb"><span>Domain</span><input name="domain" placeholder="Optional"></label>
<label data-provider-fields="sftp smb webdav"><span>Username</span><input name="username" required></label>
<label data-provider-fields="sftp smb webdav"><span>Password</span><input name="password" type="password"></label>
<label data-provider-fields="sftp"><span>Private key</span><textarea name="private_key" rows="4" placeholder="Optional private key"></textarea></label>
<label data-provider-fields="sftp"><span>SSH host public key</span><textarea name="host_key" rows="3" placeholder="Optional pinned host key"></textarea></label>
<label data-provider-fields="webdav"><span>WebDAV URL</span><input name="endpoint" placeholder="https://files.example.com/webdav"></label>
<label data-provider-fields="sftp smb webdav"><span>Remote path</span><input name="remote_path" placeholder="/srv/warpbox"></label>
{{/* View-mode summary */}}
<div class="storage-card-summary">
{{if eq .Config.Type "local"}}
<div class="storage-detail"><span>Path</span><code>{{.Config.LocalPath}}</code></div>
{{else if or (eq .Config.Type "s3") (eq .Config.Provider "contabo")}}
{{if .Config.Endpoint}}<div class="storage-detail"><span>Endpoint</span><span>{{.Config.Endpoint}}</span></div>{{end}}
{{if .Config.Bucket}}<div class="storage-detail"><span>Bucket</span><span>{{.Config.Bucket}}</span></div>{{end}}
{{if .Config.Region}}<div class="storage-detail"><span>Region</span><span>{{.Config.Region}}</span></div>{{end}}
{{if .Config.AccessKey}}<div class="storage-detail"><span>Access key</span><span>{{.Config.AccessKey}}</span></div>{{end}}
{{else if eq .Config.Type "sftp"}}
{{if .Config.Host}}<div class="storage-detail"><span>Host</span><span>{{.Config.Host}}{{if .Config.Port}}:{{.Config.Port}}{{end}}</span></div>{{end}}
{{if .Config.Username}}<div class="storage-detail"><span>Username</span><span>{{.Config.Username}}</span></div>{{end}}
{{if .Config.RemotePath}}<div class="storage-detail"><span>Remote path</span><span>{{.Config.RemotePath}}</span></div>{{end}}
{{else if eq .Config.Type "smb"}}
{{if .Config.Host}}<div class="storage-detail"><span>Host</span><span>{{if .Config.Domain}}{{.Config.Domain}}\{{end}}{{.Config.Username}}@{{.Config.Host}}/{{.Config.Share}}</span></div>{{end}}
{{if .Config.RemotePath}}<div class="storage-detail"><span>Remote path</span><span>{{.Config.RemotePath}}</span></div>{{end}}
{{else if eq .Config.Type "webdav"}}
{{if .Config.Endpoint}}<div class="storage-detail"><span>URL</span><span>{{.Config.Endpoint}}</span></div>{{end}}
{{if .Config.Username}}<div class="storage-detail"><span>Username</span><span>{{.Config.Username}}</span></div>{{end}}
{{if .Config.RemotePath}}<div class="storage-detail"><span>Remote path</span><span>{{.Config.RemotePath}}</span></div>{{end}}
{{end}}
{{if not (.Config.LastTestedAt.IsZero)}}
<div class="storage-detail storage-detail-test {{if .Config.LastTestSuccess}}is-ok{{else}}is-err{{end}}">
<span>Last test</span>
<span>{{.Config.LastTestedAt.Format "Jan 2, 15:04"}} · {{if .Config.LastTestSuccess}}Passed{{else}}{{if .Config.LastTestError}}{{.Config.LastTestError}}{{else}}Failed{{end}}{{end}}</span>
</div>
<button class="button button-primary" type="submit">Add storage</button>
</form>
{{end}}
</div>
{{/* Edit-mode form — hidden via CSS until .is-editing */}}
{{if ne .Config.ID "local"}}
<div class="storage-card-body">
<form action="/admin/storage/{{.Config.ID}}/edit" method="post" class="storage-card-fields">
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
<label><span>Storage kind</span>
<select name="provider" data-storage-provider>
<option value="s3" {{if or (eq .Config.Provider "s3") (eq .Config.Provider "")}}selected{{end}}>S3 bucket</option>
<option value="contabo" {{if eq .Config.Provider "contabo"}}selected{{end}}>Contabo Object Storage</option>
<option value="sftp" {{if eq .Config.Provider "sftp"}}selected{{end}}>SFTP</option>
<option value="smb" {{if eq .Config.Provider "smb"}}selected{{end}}>Samba</option>
<option value="webdav" {{if eq .Config.Provider "webdav"}}selected{{end}}>WebDAV</option>
</select>
</label>
<label><span>Name</span><input name="name" value="{{.Config.Name}}" required></label>
<label data-provider-fields="s3 contabo"><span>Endpoint</span><input name="endpoint" value="{{.Config.Endpoint}}" required></label>
<label data-provider-fields="s3 contabo"><span>Region</span><input name="region" value="{{.Config.Region}}"></label>
<label data-provider-fields="s3 contabo"><span>Bucket</span><input name="bucket" value="{{.Config.Bucket}}" required></label>
<label data-provider-fields="s3 contabo"><span>Access key</span><input name="access_key" value="{{.Config.AccessKey}}" required></label>
<label data-provider-fields="s3 contabo"><span>Secret key</span><input name="secret_key" type="password" placeholder="Leave unchanged"></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="use_ssl" {{if .Config.UseSSL}}checked{{end}}><span>Use TLS</span></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="path_style" {{if .Config.PathStyle}}checked{{end}}><span>Path-style lookup</span></label>
<label data-provider-fields="sftp smb"><span>Host</span><input name="host" value="{{.Config.Host}}" required></label>
<label data-provider-fields="sftp smb"><span>Port</span><input type="number" name="port" min="1" value="{{.Config.Port}}"></label>
<label data-provider-fields="smb"><span>Share</span><input name="share" value="{{.Config.Share}}" required></label>
<label data-provider-fields="smb"><span>Domain</span><input name="domain" value="{{.Config.Domain}}" placeholder="Optional"></label>
<label data-provider-fields="sftp smb webdav"><span>Username</span><input name="username" value="{{.Config.Username}}" required></label>
<label data-provider-fields="sftp smb webdav"><span>Password</span><input name="password" type="password" placeholder="Leave unchanged"></label>
<label data-provider-fields="sftp"><span>Private key</span><textarea name="private_key" rows="4" placeholder="Leave unchanged"></textarea></label>
<label data-provider-fields="sftp"><span>SSH host key</span><textarea name="host_key" rows="3" placeholder="Optional">{{.Config.HostKey}}</textarea></label>
<label data-provider-fields="webdav"><span>WebDAV URL</span><input name="endpoint" value="{{.Config.Endpoint}}" placeholder="https://files.example.com/webdav"></label>
<label data-provider-fields="sftp smb webdav"><span>Remote path</span><input name="remote_path" value="{{.Config.RemotePath}}" placeholder="/srv/warpbox"></label>
<div class="storage-card-edit-bar">
<button class="button button-primary button-sm" type="submit">Save changes</button>
<button class="button button-outline button-sm storage-cancel-trigger" type="button">Cancel</button>
</div>
</form>
</div>
{{end}}
</div>
{{end}}
{{/* Add storage section */}}
<div class="storage-add-section">
<div class="storage-add-controls">
<button class="button button-outline storage-add-trigger" type="button">
{{template "icon-plus-circle" .}}
<span>Add storage</span>
</button>
</div>
<div class="storage-type-picker" hidden>
<p class="muted-copy" style="margin:0 0 0.75rem">Choose a backend type</p>
<div class="storage-type-grid">
<button class="storage-type-option" type="button" data-provider="s3">
{{template "icon-cloud-upload" .}}
<strong>S3 Bucket</strong>
<span>Generic S3-compatible object storage</span>
</button>
<button class="storage-type-option" type="button" data-provider="contabo">
{{template "icon-cloud-upload" .}}
<strong>Contabo Object Storage</strong>
<span>Optimized settings for Contabo COS</span>
</button>
<button class="storage-type-option" type="button" data-provider="sftp">
{{template "icon-database" .}}
<strong>SFTP</strong>
<span>SSH file transfer to a server or NAS</span>
</button>
<button class="storage-type-option" type="button" data-provider="smb">
{{template "icon-folder" .}}
<strong>Samba / SMB</strong>
<span>Windows share or network attached storage</span>
</button>
<button class="storage-type-option" type="button" data-provider="webdav">
{{template "icon-cloud-sync" .}}
<strong>WebDAV</strong>
<span>Nextcloud, ownCloud, or any WebDAV server</span>
</button>
</div>
</div>
<div class="storage-new-card storage-card is-editing" hidden>
<div class="storage-card-header">
<div class="storage-card-identity">
<div class="storage-card-icon storage-new-icon">{{template "icon-cloud-upload" .}}</div>
<div>
<strong class="storage-card-name storage-new-label">New storage backend</strong>
<div class="storage-card-meta">
<span class="badge storage-new-type-badge">S3 bucket</span>
</div>
</div>
</div>
</div>
<div class="storage-card-body">
<form action="/admin/storage/s3" method="post" class="storage-card-fields">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<label><span>Storage kind</span>
<select name="provider" data-storage-provider>
<option value="s3">S3 bucket</option>
<option value="contabo">Contabo Object Storage</option>
<option value="sftp">SFTP</option>
<option value="smb">Samba</option>
<option value="webdav">WebDAV</option>
</select>
</label>
<label><span>Name</span><input name="name" placeholder="My storage" required></label>
<label data-provider-fields="s3 contabo"><span>Endpoint</span><input name="endpoint" placeholder="s3.example.com" required></label>
<label data-provider-fields="s3 contabo"><span>Region</span><input name="region" placeholder="us-east-1"></label>
<label data-provider-fields="s3 contabo"><span>Bucket</span><input name="bucket" placeholder="my-bucket" required></label>
<label data-provider-fields="s3 contabo"><span>Access key</span><input name="access_key" required></label>
<label data-provider-fields="s3 contabo"><span>Secret key</span><input name="secret_key" type="password" required></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="use_ssl" checked><span>Use TLS</span></label>
<label class="checkbox-field" data-provider-fields="s3 contabo"><input type="checkbox" name="path_style"><span>Path-style lookup</span></label>
<label data-provider-fields="sftp smb"><span>Host</span><input name="host" placeholder="files.example.com" required></label>
<label data-provider-fields="sftp smb"><span>Port</span><input type="number" name="port" min="1"></label>
<label data-provider-fields="smb"><span>Share</span><input name="share" placeholder="uploads" required></label>
<label data-provider-fields="smb"><span>Domain</span><input name="domain" placeholder="Optional"></label>
<label data-provider-fields="sftp smb webdav"><span>Username</span><input name="username" required></label>
<label data-provider-fields="sftp smb webdav"><span>Password</span><input name="password" type="password"></label>
<label data-provider-fields="sftp"><span>Private key</span><textarea name="private_key" rows="4" placeholder="Optional private key"></textarea></label>
<label data-provider-fields="sftp"><span>SSH host key</span><textarea name="host_key" rows="3" placeholder="Optional pinned host key"></textarea></label>
<label data-provider-fields="webdav"><span>WebDAV URL</span><input name="endpoint" placeholder="https://files.example.com/webdav"></label>
<label data-provider-fields="sftp smb webdav"><span>Remote path</span><input name="remote_path" placeholder="/srv/warpbox"></label>
<div class="storage-card-edit-bar">
<button class="button button-primary button-sm" type="submit">Add storage</button>
<button class="button button-outline button-sm storage-new-cancel" type="button">Cancel</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</section>