- Task A: Offline mode banner in layout (nas_offline field in LayoutTemplate)
- Task B: Safe-to-disconnect sync indicator on dashboard (all_synced field)
- Task C: Preset apply buttons (photographer/video/office) in config tab with POST /api/preset/{profile} endpoint
- Task D: Reconnect button and error banner in share detail panel
- Added nas_offline/all_synced fields to DaemonStatus for integration contract
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
139 lines
6.2 KiB
HTML
139 lines
6.2 KiB
HTML
<div x-data="{ expanded: new URLSearchParams(location.search).get('expand') || '{{ expand }}' }">
|
||
<table class="share-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Name</th>
|
||
<th>Health</th>
|
||
<th>Mount</th>
|
||
<th>Cache</th>
|
||
<th>Dirty</th>
|
||
<th>Speed</th>
|
||
<th>Transfers</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for share in shares %}
|
||
<tr class="share-row" @click="expanded = expanded === '{{ share.name }}' ? '' : '{{ share.name }}'">
|
||
<td><strong>{{ share.name }}</strong></td>
|
||
<td>
|
||
{% if share.health == "OK" %}
|
||
<span class="badge badge-ok">OK</span>
|
||
{% elif share.health == "FAILED" %}
|
||
<span class="badge badge-error">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 %}
|
||
{% if share.warmup_state == "running" %}
|
||
<span class="badge badge-warmup">WARMUP {{ share.warmup_done }}/{{ share.warmup_total }}</span>
|
||
{% elif share.warmup_state == "pending" %}
|
||
<span class="badge badge-warmup">WARMUP...</span>
|
||
{% elif share.warmup_state == "complete" %}
|
||
<span class="badge badge-ok">WARMED</span>
|
||
{% endif %}
|
||
{% if share.dir_refresh_active %}
|
||
{% if share.last_dir_refresh_ago.is_empty() %}
|
||
<span class="badge badge-warn">DIR-REFRESH...</span>
|
||
{% else %}
|
||
<span class="badge badge-ok">DIR-REFRESH {{ share.last_dir_refresh_ago }}</span>
|
||
{% endif %}
|
||
{% endif %}
|
||
</td>
|
||
<td class="mono">{{ share.mount_point }}</td>
|
||
<td>{{ share.cache_display }}</td>
|
||
<td>{{ share.dirty_count }}</td>
|
||
<td>{{ share.speed_display }}</td>
|
||
<td>{{ share.transfers }}</td>
|
||
</tr>
|
||
<tr x-show="expanded === '{{ share.name }}'" x-transition {% if share.name != expand %}x-cloak{% endif %} class="detail-row">
|
||
<td colspan="7">
|
||
<div class="detail-panel">
|
||
<div class="detail-grid">
|
||
<div class="detail-card">
|
||
<div class="label">Cache Used</div>
|
||
<div class="value">{{ share.cache_display }}</div>
|
||
</div>
|
||
<div class="detail-card">
|
||
<div class="label">Dirty Files</div>
|
||
<div class="value">{{ share.dirty_count }}</div>
|
||
</div>
|
||
<div class="detail-card">
|
||
<div class="label">Transfer Speed</div>
|
||
<div class="value">{{ share.speed_display }}</div>
|
||
</div>
|
||
<div class="detail-card">
|
||
<div class="label">Active Transfers</div>
|
||
<div class="value">{{ share.transfers }}</div>
|
||
</div>
|
||
</div>
|
||
{% if share.health == "FAILED" %}
|
||
<div class="share-error-banner">
|
||
<span class="error-icon">✗</span>
|
||
<span class="error-msg">{{ share.health_message }}</span>
|
||
<button class="action-btn-sm"
|
||
hx-post="/api/reconnect/{{ share.name }}"
|
||
hx-target="closest .share-error-banner"
|
||
hx-swap="outerHTML">
|
||
重试
|
||
</button>
|
||
</div>
|
||
{% endif %}
|
||
<div style="margin-bottom:12px">
|
||
<button class="action-btn"
|
||
hx-post="/api/reconnect/{{ share.name }}"
|
||
hx-confirm="重新连接 {{ share.name }}?"
|
||
hx-target="this"
|
||
hx-swap="outerHTML">
|
||
重新连接
|
||
</button>
|
||
</div>
|
||
<table class="info-table">
|
||
<tr><td>Health</td><td>{{ share.health }}</td></tr>
|
||
{% if share.health == "FAILED" %}
|
||
<tr><td>Probe Error</td><td class="error-text">{{ share.health_message }}</td></tr>
|
||
{% endif %}
|
||
<tr><td>Connection</td><td class="mono">{{ share.connection }}</td></tr>
|
||
<tr><td>Mount Point</td><td class="mono">{{ share.mount_point }}</td></tr>
|
||
<tr><td>Remote Path</td><td class="mono">{{ share.remote_path }}</td></tr>
|
||
<tr><td>RC Port</td><td>{{ share.rc_port }}</td></tr>
|
||
<tr><td>Errored Files</td><td>{{ share.errored_files }}</td></tr>
|
||
<tr><td>Total Errors</td><td>{{ share.errors }}</td></tr>
|
||
<tr><td>Mounted</td><td>{% if share.mounted %}Yes{% else %}No{% endif %}</td></tr>
|
||
<tr><td>Read-Only</td><td>{% if share.read_only %}Yes{% else %}No{% endif %}</td></tr>
|
||
{% if share.dir_refresh_active %}
|
||
<tr><td>Dir Refresh</td><td>{% if share.last_dir_refresh_ago.is_empty() %}pending{% else %}{{ share.last_dir_refresh_ago }}{% endif %}</td></tr>
|
||
{% endif %}
|
||
</table>
|
||
{% if !share.warmup_rules.is_empty() %}
|
||
<h4 style="margin-top:1rem;margin-bottom:0.5rem;font-size:0.95em">Warmup Rules</h4>
|
||
<table class="info-table">
|
||
<thead><tr>
|
||
<td style="font-weight:600;color:var(--text-muted)">Path</td>
|
||
<td style="font-weight:600;color:var(--text-muted)">Filter</td>
|
||
<td style="font-weight:600;color:var(--text-muted)">State</td>
|
||
<td style="font-weight:600;color:var(--text-muted)">Progress</td>
|
||
</tr></thead>
|
||
<tbody>
|
||
{% for rule in share.warmup_rules %}
|
||
<tr>
|
||
<td class="mono">{{ rule.path }}</td>
|
||
<td>{% if rule.newer_than.is_empty() %}-{% else %}{{ rule.newer_than }}{% endif %}</td>
|
||
<td><span class="badge badge-{{ rule.badge_class }}">{{ rule.state }}</span></td>
|
||
<td>{{ rule.cached + rule.skipped }}/{{ rule.total_files }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% endif %}
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|