feat: enrich activity job display with manga/chapter context
- Add mangaTitle to ScrapingJob context at creation time - Fix job.js constructor to map failureReason, attempts, maxAttempts, context from API - JobItem: show readable type label, manga name, chapter number, source and attempts counter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
19395b4869
commit
95f224d69a
@@ -7,8 +7,12 @@ export class Job {
|
|||||||
payload = {},
|
payload = {},
|
||||||
result = null,
|
result = null,
|
||||||
error = null,
|
error = null,
|
||||||
|
failureReason = null,
|
||||||
createdAt = new Date().toISOString(),
|
createdAt = new Date().toISOString(),
|
||||||
updatedAt = new Date().toISOString()
|
updatedAt = new Date().toISOString(),
|
||||||
|
attempts = 0,
|
||||||
|
maxAttempts = 1,
|
||||||
|
context = {}
|
||||||
}) {
|
}) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -16,9 +20,12 @@ export class Job {
|
|||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.error = error;
|
this.error = failureReason ?? error;
|
||||||
this.createdAt = createdAt;
|
this.createdAt = createdAt;
|
||||||
this.updatedAt = updatedAt;
|
this.updatedAt = updatedAt;
|
||||||
|
this.attempts = attempts;
|
||||||
|
this.maxAttempts = maxAttempts;
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(data) {
|
static create(data) {
|
||||||
|
|||||||
@@ -10,7 +10,12 @@
|
|||||||
<td class="py-4 px-4 text-center">
|
<td class="py-4 px-4 text-center">
|
||||||
<input type="checkbox" class="form-checkbox h-5 w-5 text-green-600" />
|
<input type="checkbox" class="form-checkbox h-5 w-5 text-green-600" />
|
||||||
</td>
|
</td>
|
||||||
<td class="py-4 px-4 font-medium">{{ job.type }}</td>
|
<td class="py-4 px-4 font-medium">
|
||||||
|
<div>{{ jobTypeLabel }}</div>
|
||||||
|
<div v-if="job.context?.mangaTitle" class="text-xs text-gray-500 mt-0.5">
|
||||||
|
{{ job.context.mangaTitle }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td class="py-4 px-4">
|
<td class="py-4 px-4">
|
||||||
<span
|
<span
|
||||||
class="px-2 py-1 text-xs rounded-full"
|
class="px-2 py-1 text-xs rounded-full"
|
||||||
@@ -27,6 +32,18 @@
|
|||||||
<div v-if="job.error" class="text-sm text-red-600">
|
<div v-if="job.error" class="text-sm text-red-600">
|
||||||
{{ job.error }}
|
{{ job.error }}
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="job.context?.mangaTitle || job.context?.chapterNumber !== undefined || job.context?.sourceId"
|
||||||
|
class="text-sm text-gray-700 space-y-0.5">
|
||||||
|
<div v-if="job.context.mangaTitle" class="font-medium">
|
||||||
|
{{ job.context.mangaTitle }}
|
||||||
|
</div>
|
||||||
|
<div v-if="job.context.chapterNumber !== undefined" class="text-gray-500">
|
||||||
|
Chapitre {{ job.context.chapterNumber }}
|
||||||
|
</div>
|
||||||
|
<div v-if="job.context.sourceId" class="text-xs text-gray-400">
|
||||||
|
Source : {{ job.context.sourceId }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-else class="text-sm text-gray-600">
|
<div v-else class="text-sm text-gray-600">
|
||||||
{{ formatDate(job.createdAt) }}
|
{{ formatDate(job.createdAt) }}
|
||||||
</div>
|
</div>
|
||||||
@@ -66,6 +83,11 @@
|
|||||||
En attente
|
En attente
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="job.maxAttempts > 1 || job.attempts > 0"
|
||||||
|
class="text-xs text-gray-400 mt-1 text-center">
|
||||||
|
{{ job.attempts }} / {{ job.maxAttempts }} tentative{{ job.maxAttempts > 1 ? 's' : '' }}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-4 px-4">
|
<td class="py-4 px-4">
|
||||||
<button
|
<button
|
||||||
@@ -79,24 +101,33 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { TrashIcon } from '@heroicons/vue/24/outline';
|
import { TrashIcon } from '@heroicons/vue/24/outline';
|
||||||
import { defineEmits, defineProps } from 'vue';
|
import { computed, defineEmits, defineProps } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
job: {
|
job: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(['delete']);
|
|
||||||
|
|
||||||
function formatDate(dateString) {
|
|
||||||
const date = new Date(dateString);
|
|
||||||
return date.toLocaleString();
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function onDelete() {
|
const emit = defineEmits(['delete']);
|
||||||
emit('delete', props.job.id);
|
|
||||||
}
|
const JOB_TYPE_LABELS = {
|
||||||
|
scraping_job: 'Scraping',
|
||||||
|
conversion_job: 'Conversion',
|
||||||
|
};
|
||||||
|
|
||||||
|
const jobTypeLabel = computed(() =>
|
||||||
|
JOB_TYPE_LABELS[props.job.type] ?? props.job.type
|
||||||
|
);
|
||||||
|
|
||||||
|
function formatDate(dateString) {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toLocaleString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDelete() {
|
||||||
|
emit('delete', props.job.id);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ readonly class ScrapeChapterHandler
|
|||||||
// Ajout de l'ID du chapitre et du slug dans le contexte du job
|
// Ajout de l'ID du chapitre et du slug dans le contexte du job
|
||||||
$job->context['chapterId'] = $command->chapterId;
|
$job->context['chapterId'] = $command->chapterId;
|
||||||
$job->context['slug'] = $slug;
|
$job->context['slug'] = $slug;
|
||||||
|
$job->context['mangaTitle'] = $manga->getTitle();
|
||||||
|
|
||||||
$job->start();
|
$job->start();
|
||||||
$this->jobRepository->save($job);
|
$this->jobRepository->save($job);
|
||||||
|
|||||||
Reference in New Issue
Block a user