slides: fix manager thumbnail 404 under /ops + single-column order list

SlideManager rendered <img :src=slide.url> raw, so the root-relative
/api/slides/img/... path 404'd under the /ops subpath mount (the /tv display
already wrapped withBase; the manager did not). Wrap the thumbnail in withBase.
Also switch the multi-column grid to a single-column list with order numbers so
the top-to-bottom play order is clear to arrange.
This commit is contained in:
cproudlock
2026-07-20 14:16:12 -04:00
parent 8496441ddc
commit a50e5b0ec1

View File

@@ -32,18 +32,15 @@
<div v-if="loading" class="muted">Loading...</div>
<div v-else-if="!slides.length" class="muted empty">No slides yet. Upload some images.</div>
<div v-else class="slide-grid">
<div v-for="(slide, idx) in slides" :key="slide.slideid" class="slide-tile">
<label class="pick">
<input type="checkbox" :value="slide.filename" v-model="selected" />
</label>
<img :src="slide.url" :alt="slide.filename" class="thumb" />
<div class="slide-meta">
<span class="fname" :title="slide.filename">{{ slide.filename }}</span>
<div class="move">
<button class="btn btn-sm btn-secondary" :disabled="idx === 0" @click="move(idx, -1)" title="Move up">&uarr;</button>
<button class="btn btn-sm btn-secondary" :disabled="idx === slides.length - 1" @click="move(idx, 1)" title="Move down">&darr;</button>
</div>
<div v-else class="slide-list">
<div v-for="(slide, idx) in slides" :key="slide.slideid" class="slide-row">
<input type="checkbox" class="pick" :value="slide.filename" v-model="selected" />
<span class="order-num">{{ idx + 1 }}</span>
<img :src="withBase(slide.url)" :alt="slide.filename" class="thumb" />
<span class="fname" :title="slide.filename">{{ slide.filename }}</span>
<div class="move">
<button class="btn btn-sm btn-secondary" :disabled="idx === 0" @click="move(idx, -1)" title="Move up">&uarr;</button>
<button class="btn btn-sm btn-secondary" :disabled="idx === slides.length - 1" @click="move(idx, 1)" title="Move down">&darr;</button>
</div>
</div>
</div>
@@ -54,6 +51,7 @@
<script setup>
import { ref, computed, onMounted } from 'vue'
import { slidesApi } from '@/api'
import { withBase } from '@/utils/basePath'
const surfaces = [
{ key: 'lobby', label: 'Lobby Display' },
@@ -164,42 +162,40 @@ onMounted(load)
.muted { color: var(--text-light); }
.empty { padding: 30px 0; text-align: center; }
.slide-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 14px;
.slide-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.slide-tile {
position: relative;
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
background: var(--bg);
}
.slide-tile .pick {
position: absolute;
top: 6px;
left: 6px;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
padding: 2px 4px;
}
.thumb {
width: 100%;
height: 130px;
object-fit: cover;
display: block;
background: #000;
}
.slide-meta {
.slide-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 6px 8px;
gap: 12px;
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 12px;
background: var(--bg);
}
.slide-row .pick { flex-shrink: 0; }
.order-num {
width: 1.6rem;
flex-shrink: 0;
text-align: center;
font-weight: 700;
color: var(--text-light);
}
.thumb {
width: 120px;
height: 68px;
flex-shrink: 0;
object-fit: cover;
border-radius: 4px;
background: #000;
}
.fname {
font-size: 0.78rem;
flex: 1;
min-width: 0;
font-size: 0.82rem;
font-family: monospace;
overflow: hidden;
text-overflow: ellipsis;