- Replace raw TOML textarea with Alpine.js interactive form editor (10 collapsible sections with change-tier badges, dynamic array management for connections/shares/ warmup rules, proper input controls per field type) - Add SSE-based live dashboard updates replacing htmx polling - Add log viewer tab with ring buffer backend and incremental polling - Fix SMB not seeing new shares after config reload: kill entire smbd process group (not just parent PID) so forked workers release port 445 - Add SIGHUP-based smbd config reload for share changes instead of full restart, preserving existing client connections - Generate human-readable commented TOML from config editor instead of bare toml::to_string_pretty() output - Fix Alpine.js 2.x __x.$data calls in dashboard/share templates (now Alpine 3.x) - Fix toggle switch CSS overlap with field labels - Fix dashboard going blank on tab switch (remove hx-swap-oob from tab content) - Add htmx:afterSettle → Alpine.initTree() bridge for robust tab switching Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
66 lines
2.6 KiB
HTML
66 lines
2.6 KiB
HTML
<div id="dashboard-stats">
|
|
<div class="stat-cards">
|
|
<div class="stat-card">
|
|
<div class="label">Shares</div>
|
|
<div class="value">{{ healthy_count }} / {{ total_shares }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="label">Cache</div>
|
|
<div class="value">{{ total_cache_display }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="label">Speed</div>
|
|
<div class="value">{{ aggregate_speed_display }}</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="label">Transfers</div>
|
|
<div class="value">{{ active_transfers }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="share-rows">
|
|
<div class="cards">
|
|
{% for share in shares %}
|
|
<div class="card" style="cursor:pointer"
|
|
hx-get="/tabs/shares?expand={{ share.name }}" hx-target="#tab-content" hx-swap="innerHTML"
|
|
@click="activeTab = 'shares'">
|
|
<div class="card-header">
|
|
<h2>{{ share.name }}</h2>
|
|
<div>
|
|
{% if share.health == "OK" %}
|
|
<span class="badge badge-ok">OK</span>
|
|
{% elif share.health == "FAILED" %}
|
|
<span class="badge badge-error" title="{{ share.health_message }}">FAILED</span>
|
|
{% elif share.health == "PROBING" %}
|
|
<span class="badge badge-warn">PROBING</span>
|
|
{% else %}
|
|
<span class="badge badge-warn">PENDING</span>
|
|
{% endif %}
|
|
{% if share.read_only %}
|
|
<span class="badge badge-ro">RO</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="stats">
|
|
<span><span class="label">Mount:</span> <span class="value">{{ share.mount_point }}</span></span>
|
|
<span><span class="label">Cache:</span> <span class="value">{{ share.cache_display }}</span></span>
|
|
<span><span class="label">Dirty:</span> <span class="value">{{ share.dirty_count }}</span></span>
|
|
<span><span class="label">Speed:</span> <span class="value">{{ share.speed_display }}</span></span>
|
|
</div>
|
|
{% if share.health == "FAILED" %}
|
|
<div class="error-msg">{{ share.health_message }}</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<div id="protocol-badges">
|
|
<div class="protocols">
|
|
<span class="proto-badge {% if smbd_running %}proto-on{% else %}proto-off{% endif %}">SMB: {% if smbd_running %}ON{% else %}OFF{% endif %}</span>
|
|
<span class="proto-badge {% if nfs_exported %}proto-on{% else %}proto-off{% endif %}">NFS: {% if nfs_exported %}ON{% else %}OFF{% endif %}</span>
|
|
<span class="proto-badge {% if webdav_running %}proto-on{% else %}proto-off{% endif %}">WebDAV: {% if webdav_running %}ON{% else %}OFF{% endif %}</span>
|
|
</div>
|
|
</div>
|