308 lines
7.0 KiB
Vue
308 lines
7.0 KiB
Vue
<template>
|
|
<div class="recent-posters">
|
|
<!-- Loading State -->
|
|
<div v-if="isLoading" class="posters-grid">
|
|
<div v-for="i in 6" :key="i" class="poster-skeleton">
|
|
<q-skeleton type="rect" class="skeleton-image" />
|
|
<div class="skeleton-content">
|
|
<q-skeleton type="text" width="70%" />
|
|
<q-skeleton type="text" width="40%" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<div v-else-if="posters.length === 0" class="empty-state">
|
|
<q-icon name="image" size="64px" color="grey-4" />
|
|
<h3>Nessuna locandina</h3>
|
|
<p>Crea la tua prima locandina per vederla qui</p>
|
|
<q-btn
|
|
color="primary"
|
|
icon="add"
|
|
label="Crea Locandina"
|
|
@click="$router.push('/posters/poster-generator')"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Posters Grid -->
|
|
<div v-else class="posters-grid">
|
|
<div
|
|
v-for="poster in posters"
|
|
:key="poster._id"
|
|
class="poster-card"
|
|
>
|
|
<div class="poster-image" @click="$emit('view', poster)">
|
|
<img
|
|
:src="poster.renderOutput?.png?.url || poster.renderOutput?.jpg?.url || '/placeholder.png'"
|
|
:alt="poster.name"
|
|
loading="lazy"
|
|
/>
|
|
<div class="poster-overlay">
|
|
<q-btn round color="white" text-color="dark" icon="visibility" size="sm" />
|
|
</div>
|
|
|
|
<q-badge
|
|
v-if="poster.metadata?.isFavorite"
|
|
color="amber"
|
|
floating
|
|
class="favorite-badge"
|
|
>
|
|
<q-icon name="star" size="12px" />
|
|
</q-badge>
|
|
</div>
|
|
|
|
<div class="poster-info">
|
|
<h3 :title="poster.name">{{ poster.name }}</h3>
|
|
<p class="poster-date">
|
|
<q-icon name="schedule" size="14px" />
|
|
{{ formatDate(poster.createdAt) }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="poster-actions">
|
|
<q-btn flat dense round icon="download" size="sm" @click="$emit('download', poster)">
|
|
<q-tooltip>Scarica</q-tooltip>
|
|
</q-btn>
|
|
<q-btn flat dense round icon="edit" size="sm" @click="$emit('edit', poster)">
|
|
<q-tooltip>Modifica</q-tooltip>
|
|
</q-btn>
|
|
<q-btn flat dense round icon="more_vert" size="sm">
|
|
<q-menu>
|
|
<q-list dense>
|
|
<q-item clickable v-close-popup @click="$emit('view', poster)">
|
|
<q-item-section avatar><q-icon name="visibility" size="20px" /></q-item-section>
|
|
<q-item-section>Visualizza</q-item-section>
|
|
</q-item>
|
|
<q-item clickable v-close-popup @click="sharePoster(poster)">
|
|
<q-item-section avatar><q-icon name="share" size="20px" /></q-item-section>
|
|
<q-item-section>Condividi</q-item-section>
|
|
</q-item>
|
|
<q-separator />
|
|
<q-item clickable v-close-popup @click="deletePoster(poster)" class="text-negative">
|
|
<q-item-section avatar><q-icon name="delete" size="20px" /></q-item-section>
|
|
<q-item-section>Elimina</q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
</q-menu>
|
|
</q-btn>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useQuasar } from 'quasar';
|
|
|
|
const props = defineProps<{
|
|
posters: any[];
|
|
isLoading: boolean;
|
|
}>();
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'view', poster: any): void;
|
|
(e: 'download', poster: any): void;
|
|
(e: 'edit', poster: any): void;
|
|
}>();
|
|
|
|
const $q = useQuasar();
|
|
|
|
const formatDate = (dateString: string) => {
|
|
const date = new Date(dateString);
|
|
const now = new Date();
|
|
const diff = now.getTime() - date.getTime();
|
|
|
|
const minutes = Math.floor(diff / 60000);
|
|
const hours = Math.floor(diff / 3600000);
|
|
const days = Math.floor(diff / 86400000);
|
|
|
|
if (minutes < 60) return `${minutes} min fa`;
|
|
if (hours < 24) return `${hours} ore fa`;
|
|
if (days < 7) return `${days} giorni fa`;
|
|
|
|
return date.toLocaleDateString('it-IT', {
|
|
day: 'numeric',
|
|
month: 'short'
|
|
});
|
|
};
|
|
|
|
const sharePoster = async (poster: any) => {
|
|
try {
|
|
if (navigator.share) {
|
|
await navigator.share({
|
|
title: poster.name,
|
|
text: `Guarda la mia locandina: ${poster.content?.title || poster.name}`,
|
|
url: window.location.origin + `/posters/${poster._id}`
|
|
});
|
|
} else {
|
|
await navigator.clipboard.writeText(window.location.origin + `/posters/${poster._id}`);
|
|
$q.notify({ type: 'positive', message: 'Link copiato!' });
|
|
}
|
|
} catch (error) {
|
|
console.error('Share error:', error);
|
|
}
|
|
};
|
|
|
|
const deletePoster = (poster: any) => {
|
|
$q.dialog({
|
|
title: 'Elimina Locandina',
|
|
message: `Vuoi eliminare "${poster.name}"?`,
|
|
cancel: true
|
|
}).onOk(() => {
|
|
$q.notify({ type: 'info', message: 'Eliminazione...' });
|
|
// Implement delete
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.recent-posters {
|
|
min-height: 200px;
|
|
}
|
|
|
|
.posters-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 1.25rem;
|
|
|
|
@media (max-width: 900px) {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
@media (max-width: 500px) {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
.poster-card {
|
|
background: white;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
|
transition: all 0.3s ease;
|
|
|
|
&:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
|
|
|
.poster-overlay {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.poster-image {
|
|
position: relative;
|
|
aspect-ratio: 3/4;
|
|
cursor: pointer;
|
|
overflow: hidden;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
&:hover img {
|
|
transform: scale(1.05);
|
|
}
|
|
}
|
|
|
|
.poster-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.favorite-badge {
|
|
top: 0.5rem !important;
|
|
right: 0.5rem !important;
|
|
}
|
|
|
|
.poster-info {
|
|
padding: 1rem;
|
|
|
|
h3 {
|
|
margin: 0;
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.poster-date {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
margin: 0.35rem 0 0;
|
|
font-size: 0.8rem;
|
|
color: #888;
|
|
}
|
|
}
|
|
|
|
.poster-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 0 0.5rem 0.75rem;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
// Skeleton
|
|
.poster-skeleton {
|
|
background: white;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
|
|
.skeleton-image {
|
|
height: 200px;
|
|
}
|
|
|
|
.skeleton-content {
|
|
padding: 1rem;
|
|
}
|
|
}
|
|
|
|
// Empty State
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 3rem 1rem;
|
|
background: white;
|
|
border-radius: 16px;
|
|
|
|
h3 {
|
|
margin: 1rem 0 0.5rem;
|
|
font-size: 1.2rem;
|
|
color: #555;
|
|
}
|
|
|
|
p {
|
|
color: #888;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
}
|
|
|
|
// Dark mode
|
|
.body--dark {
|
|
.poster-card,
|
|
.poster-skeleton,
|
|
.empty-state {
|
|
background: #1e1e1e;
|
|
}
|
|
|
|
.poster-info h3 {
|
|
color: #fff;
|
|
}
|
|
|
|
.empty-state h3 {
|
|
color: #ddd;
|
|
}
|
|
}
|
|
</style>
|