style(conversion): aligner l'UI de conversion sur le design system import

Ajout du Toolbar avec titre et bouton d'action, restructuration en sections
avec bordures et titres typographiques, harmonisation des espacements et
classes Tailwind avec NewImportPage.vue.
This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2026-03-15 20:24:05 +01:00
parent 2243716800
commit 9f83f9c137

View File

@@ -1,84 +1,77 @@
<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">
<div class="flex flex-col h-full">
<Toolbar :config="toolbarConfig" />
<FileUploadArea
:selected-file="conversionStore.currentFile"
:disabled="conversionStore.isProcessing"
@file-selected="handleFileSelected"
@file-cleared="handleFileClear"
/>
<div class="overflow-y-auto flex-1">
<div class="px-6 py-8">
<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>
<!-- Zone d'upload -->
<section class="border-t border-gray-200 dark:border-gray-700 pt-6">
<h2 class="text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-4">Fichier</h2>
<FileUploadArea
:selected-file="conversionStore.currentFile"
:disabled="conversionStore.isProcessing"
@file-selected="handleFileSelected"
@file-cleared="handleFileClear"
/>
</section>
<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"
/>
<!-- Progression -->
<section v-if="showProgress" class="border-t border-gray-200 dark:border-gray-700 pt-6 mt-6">
<ConversionProgress
: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"
/>
</section>
<!-- Historique -->
<section v-if="conversionStore.conversionCount > 0" class="border-t border-gray-200 dark:border-gray-700 pt-6 mt-6">
<div class="flex items-center justify-between mb-3">
<h2 class="text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider">Historique</h2>
<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-100 dark:divide-gray-700/50">
<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>
</section>
<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 Toolbar from '../../../../shared/components/ui/Toolbar.vue';
import { useConversionStore } from '../../application/store/conversionStore';
import { useNotifications } from '../../../../shared/composables/useNotifications';
import ConversionProgress from '../components/ConversionProgress.vue';
@@ -88,53 +81,68 @@ const conversionStore = useConversionStore();
const { showSuccess, showError } = useNotifications();
const showProgress = computed(() =>
conversionStore.hasSelectedFile &&
(conversionStore.isProcessing || conversionStore.hasSucceeded || conversionStore.hasError)
conversionStore.hasSelectedFile &&
(conversionStore.isProcessing || conversionStore.hasSucceeded || conversionStore.hasError)
);
const toolbarConfig = computed(() => ({
leftSection: [
{ type: 'label', text: 'Conversion CBR CBZ', class: 'text-sm font-medium' },
],
rightSection: [
...(conversionStore.hasSelectedFile && !conversionStore.hasSucceeded ? [{
type: 'button',
icon: ArrowPathIcon,
label: conversionStore.isProcessing ? 'Conversion en cours...' : 'Convertir en CBZ',
onClick: handleConvert,
disabled: conversionStore.isProcessing,
}] : []),
],
}));
const handleFileSelected = (file) => {
conversionStore.selectFile(file);
conversionStore.selectFile(file);
};
const handleFileClear = () => {
conversionStore.resetConversion();
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');
}
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]}`;
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));
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%';
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());