Refactor media gallery.

This commit is contained in:
Kailash Nadh 2025-08-02 00:07:50 +05:30
parent ba24c64fae
commit a8c842a350
2 changed files with 174 additions and 50 deletions

View file

@ -968,6 +968,127 @@ section.analytics {
.ext {
text-transform: uppercase;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1.5rem;
padding: 1rem 0;
}
.item {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
}
.thumb {
position: relative;
aspect-ratio: 1;
overflow: hidden;
}
.thumb-link {
display: block;
width: 100%;
height: 100%;
}
.thumb-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #f8f9fa;
}
.thumb-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.2s ease;
.item:hover & {
transform: scale(1.05);
}
}
.thumb-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.file-extension {
font-size: 1.2rem;
font-weight: 600;
text-transform: uppercase;
}
.media-actions {
position: absolute;
top: 8px;
right: 8px;
opacity: 0;
transition: opacity 0.2s ease;
.media-item:hover & {
opacity: 1;
}
}
.delete-btn {
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s ease;
&:hover {
background: rgba(255, 255, 255, 1);
}
}
.info {
padding: 1rem;
}
.filename {
font-weight: 600;
margin-bottom: 0.5rem;
color: #363636;
font-size: 0.9rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.date {
font-size: 0.8rem;
color: #7a7a7a;
margin-bottom: 0;
}
.pagination-wrapper {
display: flex;
justify-content: center;
}
}
/* Template form */

View file

@ -38,61 +38,64 @@
</section>
<section class="wrap gallery mt-6">
<b-table :data="media.results" :hoverable="true" :loading="loading.media" default-sort="createdAt" :paginated="true"
backend-pagination pagination-position="both" @page-change="onPageChange" :current-page="media.page"
:per-page="media.perPage" :total="media.total">
<template #top-left>
<div class="columns">
<div class="column is-6">
<form @submit.prevent="onQueryMedia">
<div>
<b-field>
<b-input v-model="queryParams.query" name="query" expanded icon="magnify" ref="query"
data-cy="query" />
<p class="controls">
<b-button native-type="submit" type="is-primary" icon-left="magnify" data-cy="btn-query" />
</p>
</b-field>
<div class="columns mb-4">
<div class="column is-6">
<form @submit.prevent="onQueryMedia">
<div>
<b-field>
<b-input v-model="queryParams.query" name="query" expanded icon="magnify" ref="query" data-cy="query" />
<p class="controls">
<b-button native-type="submit" type="is-primary" icon-left="magnify" data-cy="btn-query" />
</p>
</b-field>
</div>
</form>
</div>
</div>
<div v-if="loading.media" class="has-text-centered py-6">
<b-loading :active="loading.media" />
</div>
<div v-else-if="media.results && media.results.length > 0" class="grid">
<div v-for="item in media.results" :key="item.id" class="item">
<div class="thumb">
<a @click="(e) => onMediaSelect(item, e)" :href="item.url" target="_blank" rel="noopener noreferer"
class="thumb-link">
<div class="thumb-container">
<img v-if="item.thumbUrl" :src="item.thumbUrl" :title="item.filename" alt="" class="thumb-image" />
<div v-else class="thumb-placeholder">
<span class="file-extension">
{{ item.filename.split(".").pop().toUpperCase() }}
</span>
</div>
</form>
</div>
</a>
<div class="actions">
<a href="#" @click.prevent="$utils.confirm(null, () => onDeleteMedia(item.id))" data-cy="btn-delete"
:aria-label="$t('globals.buttons.delete')" class="delete-btn">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
</div>
</div>
</template>
<div class="info">
<p class="filename" :title="item.filename">{{ item.filename }}</p>
<p class="date">{{ $utils.niceDate(item.createdAt, true) }}</p>
</div>
</div>
</div>
<b-table-column v-slot="props" field="name" width="40%" :label="$t('globals.fields.name')">
<a @click="(e) => onMediaSelect(props.row, e)" :href="props.row.url" target="_blank" rel="noopener noreferer"
class="link" :title="props.row.filename">
{{ props.row.filename }}
</a>
</b-table-column>
<!-- Empty State -->
<div v-else-if="!loading.media">
<empty-placeholder />
</div>
<b-table-column v-slot="props" field="thumb" width="30%">
<a @click="(e) => onMediaSelect(props.row, e)" :href="props.row.url" target="_blank" rel="noopener noreferer"
class="thumb box">
<img v-if="props.row.thumbUrl" :src="props.row.thumbUrl" :title="props.row.filename" alt="" />
<span v-else class="ext">
{{ props.row.filename.split(".").pop() }}
</span>
</a>
</b-table-column>
<b-table-column v-slot="props" field="created_at" width="25%" :label="$t('globals.fields.createdAt')" sortable>
{{ $utils.niceDate(props.row.createdAt, true) }}
</b-table-column>
<b-table-column v-slot="props" field="actions" width="5%" cell-class="has-text-right">
<a href="#" @click.prevent="$utils.confirm(null, () => onDeleteMedia(props.row.id))" data-cy="btn-delete"
:aria-label="$t('globals.buttons.delete')">
<b-tooltip :label="$t('globals.buttons.delete')" type="is-dark">
<b-icon icon="trash-can-outline" size="is-small" />
</b-tooltip>
</a>
</b-table-column>
<template #empty v-if="!loading.media">
<empty-placeholder />
</template>
</b-table>
<!-- Pagination -->
<div v-if="media.total > media.perPage" class="pagination-wrapper mt-5">
<b-pagination :total="media.total" :current.sync="media.page" :per-page="media.perPage"
@change="onPageChange" />
</div>
</section>
</section>
</template>