Files
Mangarr/assets/vue/app/domain/conversion/presentation/pages/ConversionPage.vue
ext.jeremy.guillot@maxicoffee.domains cc702cff19
All checks were successful
Deploy / deploy (push) Successful in 2m46s
style(header): ajouter bouton toggle dark mode dans le header
feat(conversion): simplifier ConversionPage et brancher les toasts
style(manga): réécriture de la liste de résultats dans AddManga
chore(task): ajouter tâche conversion CBR→CBZ dans TASK.md
2026-03-14 02:17:24 +01:00

142 lines
5.3 KiB
Vue

<template>
<div class="flex flex-col h-full bg-gray-50 dark:bg-gray-900">
<div class="overflow-y-auto flex-1">
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8">
<FileUploadArea
:selected-file="conversionStore.currentFile"
:disabled="conversionStore.isProcessing"
@file-selected="handleFileSelected"
@file-cleared="handleFileClear"
/>
<div v-if="conversionStore.hasSelectedFile && !conversionStore.hasSucceeded" class="mt-6 flex justify-center">
<button
@click="handleConvert"
:disabled="conversionStore.isProcessing"
:class="[
'flex items-center gap-2 px-6 py-3 text-white font-medium rounded-lg transition-colors',
conversionStore.isProcessing
? 'bg-gray-400 cursor-not-allowed'
: 'bg-green-600 hover:bg-green-700'
]"
>
<ArrowPathIcon :class="['w-5 h-5', conversionStore.isProcessing && 'animate-spin']" />
{{ conversionStore.isProcessing ? 'Conversion en cours...' : 'Convertir en CBZ' }}
</button>
</div>
<ConversionProgress
v-if="showProgress"
class="mt-6"
:is-converting="conversionStore.isProcessing"
:progress="conversionStore.conversionProgress"
:is-success="conversionStore.hasSucceeded"
:has-error="conversionStore.hasError"
:error-message="conversionStore.conversionError"
:file-name="conversionStore.currentFileName"
:original-size="conversionStore.currentFile?.size || 0"
:converted-size="conversionStore.convertedFile?.size || 0"
@download="handleDownload"
@reset="handleReset"
/>
<div v-if="conversionStore.conversionCount > 0" class="mt-8">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300">Historique</h3>
<button
@click="conversionStore.clearHistory()"
class="text-sm text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors"
>
Effacer
</button>
</div>
<div class="divide-y divide-gray-200 dark:divide-gray-700">
<div
v-for="(conversion, index) in conversionStore.conversionHistory"
:key="index"
class="flex items-center justify-between py-3"
>
<div>
<p class="text-sm text-gray-900 dark:text-gray-100">{{ conversion.originalName }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400">{{ formatDate(conversion.timestamp) }}</p>
</div>
<div class="text-right text-sm">
<p class="text-gray-600 dark:text-gray-300">
{{ formatFileSize(conversion.originalSize) }} {{ formatFileSize(conversion.convertedSize) }}
</p>
<p class="text-xs text-green-600">{{ calculateSaving(conversion.originalSize, conversion.convertedSize) }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ArrowPathIcon } from '@heroicons/vue/24/outline';
import { computed, onMounted } from 'vue';
import { useConversionStore } from '../../application/store/conversionStore';
import { useNotifications } from '../../../../shared/composables/useNotifications';
import ConversionProgress from '../components/ConversionProgress.vue';
import FileUploadArea from '../components/FileUploadArea.vue';
const conversionStore = useConversionStore();
const { showSuccess, showError } = useNotifications();
const showProgress = computed(() =>
conversionStore.hasSelectedFile &&
(conversionStore.isProcessing || conversionStore.hasSucceeded || conversionStore.hasError)
);
const handleFileSelected = (file) => {
conversionStore.selectFile(file);
};
const handleFileClear = () => {
conversionStore.resetConversion();
};
const handleConvert = async () => {
if (!conversionStore.currentFile) return;
const success = await conversionStore.convertCurrentFile();
if (success) {
showSuccess('Conversion réussie !');
} else {
showError(conversionStore.conversionError ?? 'Échec de la conversion');
}
};
const handleDownload = () => conversionStore.downloadConvertedFile();
const handleReset = () => conversionStore.resetConversion();
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 octets';
const k = 1024;
const sizes = ['octets', 'Ko', 'Mo', 'Go'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
};
const formatDate = (isoString) =>
new Intl.DateTimeFormat('fr-FR', {
day: 'numeric',
month: 'short',
hour: '2-digit',
minute: '2-digit',
}).format(new Date(isoString));
const calculateSaving = (originalSize, convertedSize) => {
if (!originalSize || !convertedSize) return '';
const saving = ((originalSize - convertedSize) / originalSize) * 100;
if (saving > 0) return `-${saving.toFixed(1)}%`;
if (saving < 0) return `+${Math.abs(saving).toFixed(1)}%`;
return '0%';
};
onMounted(() => conversionStore.resetConversion());
</script>