style(manga-table): supprimer wrapper card + hover vert + icônes Bookmark
This commit is contained in:
parent
15cb59e420
commit
ac19cc53ca
@@ -0,0 +1,208 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="border-t border-gray-200 dark:border-gray-700">
|
||||||
|
<table class="w-full text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr class="bg-gray-50 dark:bg-gray-700/50 border-b border-gray-200 dark:border-gray-700 text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||||
|
<th class="w-10 px-4 py-3"></th>
|
||||||
|
<th class="py-3 pr-4 text-left font-medium">Titre</th>
|
||||||
|
<th class="py-3 pr-4 text-left font-medium w-44">Source préférée</th>
|
||||||
|
<th class="py-3 pr-4 text-left font-medium w-44">Chapitres</th>
|
||||||
|
<th class="py-3 px-4 text-right font-medium w-28">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-100 dark:divide-gray-700">
|
||||||
|
<tr
|
||||||
|
v-for="manga in mangas"
|
||||||
|
:key="manga.id"
|
||||||
|
class="hover:bg-gray-50 dark:hover:bg-gray-700/40 transition-colors">
|
||||||
|
|
||||||
|
<!-- Monitoring -->
|
||||||
|
<td class="px-4 py-3 text-center">
|
||||||
|
<button
|
||||||
|
:title="manga.monitored ? 'Monitoring actif — cliquer pour désactiver' : 'Monitoring inactif — cliquer pour activer'"
|
||||||
|
:class="manga.monitored
|
||||||
|
? 'text-green-500 hover:text-green-600'
|
||||||
|
: 'text-gray-300 dark:text-gray-600 hover:text-gray-400 dark:hover:text-gray-500'"
|
||||||
|
class="transition-colors"
|
||||||
|
@click="doToggleMonitoring(manga)">
|
||||||
|
<component
|
||||||
|
:is="manga.monitored ? BookmarkIcon : BookmarkSlashIcon"
|
||||||
|
class="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Titre -->
|
||||||
|
<td class="py-3 pr-4">
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'manga-details', params: { id: manga.id } }"
|
||||||
|
class="font-medium text-gray-900 dark:text-gray-100 hover:text-green-500 dark:hover:text-green-400 transition-colors">
|
||||||
|
{{ manga.title }}
|
||||||
|
</RouterLink>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Source préférée -->
|
||||||
|
<td class="py-3 pr-4">
|
||||||
|
<MangaPreferredSourceCell :manga-id="manga.id" />
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Chapitres — barre de progression -->
|
||||||
|
<td class="py-3 pr-4">
|
||||||
|
<div v-if="manga.chaptersTotal > 0">
|
||||||
|
<div class="flex items-center justify-between mb-1">
|
||||||
|
<span class="text-xs tabular-nums text-gray-500 dark:text-gray-400">
|
||||||
|
{{ manga.chaptersScraped }} / {{ manga.chaptersTotal }}
|
||||||
|
</span>
|
||||||
|
<span class="text-xs text-gray-400 dark:text-gray-500">
|
||||||
|
{{ progressPercent(manga) }}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="w-full bg-gray-100 dark:bg-gray-600 rounded-full h-1.5">
|
||||||
|
<div
|
||||||
|
class="h-1.5 rounded-full transition-all"
|
||||||
|
:class="progressPercent(manga) >= 100
|
||||||
|
? 'bg-green-500'
|
||||||
|
: 'bg-blue-500'"
|
||||||
|
:style="{ width: progressPercent(manga) + '%' }" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span v-else class="text-gray-400 dark:text-gray-600 text-xs">—</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Actions -->
|
||||||
|
<td class="py-3 px-4">
|
||||||
|
<div class="flex items-center justify-end gap-0.5">
|
||||||
|
<button
|
||||||
|
class="p-1.5 rounded-md text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors"
|
||||||
|
title="Éditer"
|
||||||
|
@click="openEdit(manga)">
|
||||||
|
<PencilIcon class="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="p-1.5 rounded-md text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors"
|
||||||
|
title="Sources préférées"
|
||||||
|
@click="openSources(manga)">
|
||||||
|
<Cog6ToothIcon class="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="p-1.5 rounded-md transition-colors"
|
||||||
|
:class="refreshingId === manga.id
|
||||||
|
? 'text-blue-400 cursor-not-allowed'
|
||||||
|
: 'text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600'"
|
||||||
|
title="Rafraîchir"
|
||||||
|
:disabled="refreshingId === manga.id"
|
||||||
|
@click="doRefresh(manga)">
|
||||||
|
<ArrowPathIcon
|
||||||
|
class="w-4 h-4"
|
||||||
|
:class="{ 'animate-spin': refreshingId === manga.id }" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modales -->
|
||||||
|
<MangaEditModal
|
||||||
|
:is-open="isEditModalOpen"
|
||||||
|
:manga="selectedManga"
|
||||||
|
:is-saving="editIsLoading"
|
||||||
|
:error="editError"
|
||||||
|
@close="closeEditModal"
|
||||||
|
@save="handleSaveEdit" />
|
||||||
|
|
||||||
|
<MangaPreferredSourcesModal
|
||||||
|
:is-open="isSourcesModalOpen"
|
||||||
|
:sources="preferredSources"
|
||||||
|
:is-loading="sourcesIsLoading"
|
||||||
|
:error="sourcesError"
|
||||||
|
:is-saving="sourcesIsSaving"
|
||||||
|
@close="isSourcesModalOpen = false"
|
||||||
|
@save="handleSaveSources" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ArrowPathIcon, BookmarkIcon, BookmarkSlashIcon, Cog6ToothIcon, PencilIcon } from '@heroicons/vue/24/outline';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { RouterLink } from 'vue-router';
|
||||||
|
import { useMangaEdit } from '../composables/useMangaEdit';
|
||||||
|
import { useMangaMonitoring } from '../composables/useMangaMonitoring';
|
||||||
|
import { useMangaPreferredSources } from '../composables/useMangaPreferredSources';
|
||||||
|
import { useMangaRefresh } from '../composables/useMangaRefresh';
|
||||||
|
import MangaEditModal from './MangaEditModal.vue';
|
||||||
|
import MangaPreferredSourceCell from './MangaPreferredSourceCell.vue';
|
||||||
|
import MangaPreferredSourcesModal from './MangaPreferredSourcesModal.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
mangas: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function progressPercent(manga) {
|
||||||
|
if (!manga.chaptersTotal) return 0;
|
||||||
|
return Math.round((manga.chaptersScraped / manga.chaptersTotal) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Monitoring ────────────────────────────────────────────
|
||||||
|
const { toggleMonitoring } = useMangaMonitoring();
|
||||||
|
|
||||||
|
async function doToggleMonitoring(manga) {
|
||||||
|
await toggleMonitoring(manga.id, !manga.monitored);
|
||||||
|
manga.monitored = !manga.monitored;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Selected manga ────────────────────────────────────────
|
||||||
|
const selectedManga = ref(null);
|
||||||
|
const isSourcesModalOpen = ref(false);
|
||||||
|
|
||||||
|
// ── Edit ──────────────────────────────────────────────────
|
||||||
|
const { isEditModalOpen, openEditModal, closeEditModal, editManga, isLoading: editIsLoading, error: editError } = useMangaEdit();
|
||||||
|
|
||||||
|
function openEdit(manga) {
|
||||||
|
selectedManga.value = manga;
|
||||||
|
openEditModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSaveEdit(data) {
|
||||||
|
if (!selectedManga.value) return;
|
||||||
|
await editManga(selectedManga.value.id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sources préférées ─────────────────────────────────────
|
||||||
|
const selectedMangaId = computed(() => selectedManga.value?.id ?? null);
|
||||||
|
const {
|
||||||
|
sources: preferredSources,
|
||||||
|
isLoading: sourcesIsLoading,
|
||||||
|
error: sourcesError,
|
||||||
|
isSaving: sourcesIsSaving,
|
||||||
|
savePreferredSources
|
||||||
|
} = useMangaPreferredSources(selectedMangaId);
|
||||||
|
|
||||||
|
function openSources(manga) {
|
||||||
|
selectedManga.value = manga;
|
||||||
|
isSourcesModalOpen.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSaveSources(sourceIds) {
|
||||||
|
savePreferredSources(sourceIds);
|
||||||
|
isSourcesModalOpen.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Refresh ───────────────────────────────────────────────
|
||||||
|
const { refreshMetadata } = useMangaRefresh();
|
||||||
|
const refreshingId = ref(null);
|
||||||
|
|
||||||
|
async function doRefresh(manga) {
|
||||||
|
if (refreshingId.value) return;
|
||||||
|
refreshingId.value = manga.id;
|
||||||
|
try {
|
||||||
|
await refreshMetadata(manga.id);
|
||||||
|
} finally {
|
||||||
|
refreshingId.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user