- Create ProBundleExportService with generateBundle() method - Generate flat ZIP with .pro file + image files at root level - Add downloadBundle() method to ServiceController - Add services.download-bundle route - Add .probundle download buttons to Information, Moderation, Sermon blocks - Add 3 tests verifying ZIP structure and validation - All tests pass (206/206, 1129 assertions)
148 lines
5.7 KiB
Vue
148 lines
5.7 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
import { router } from '@inertiajs/vue3'
|
|
import axios from 'axios'
|
|
import SlideUploader from '@/Components/SlideUploader.vue'
|
|
import SlideGrid from '@/Components/SlideGrid.vue'
|
|
import ConfirmDialog from '@/Components/ConfirmDialog.vue'
|
|
|
|
const props = defineProps({
|
|
serviceId: {
|
|
type: Number,
|
|
required: true,
|
|
},
|
|
slides: {
|
|
type: Array,
|
|
default: () => [],
|
|
},
|
|
})
|
|
|
|
const emit = defineEmits(['slides-updated'])
|
|
|
|
// Filter slides to only show moderation slides for this service
|
|
const moderationSlides = computed(() => {
|
|
return props.slides.filter(
|
|
(slide) => slide.type === 'moderation' && slide.service_id === props.serviceId
|
|
)
|
|
})
|
|
|
|
// Delete all state (#2)
|
|
const confirmingDeleteAll = ref(false)
|
|
const deletingAll = ref(false)
|
|
|
|
function handleSlideUploaded() {
|
|
emit('slides-updated')
|
|
}
|
|
|
|
function handleSlideDeleted() {
|
|
emit('slides-updated')
|
|
}
|
|
|
|
function handleSlideUpdated() {
|
|
emit('slides-updated')
|
|
}
|
|
|
|
function confirmDeleteAll() {
|
|
deletingAll.value = true
|
|
axios.delete(route('slides.bulk-destroy'), {
|
|
data: { type: 'moderation', service_id: props.serviceId },
|
|
})
|
|
.then(() => {
|
|
confirmingDeleteAll.value = false
|
|
deletingAll.value = false
|
|
emit('slides-updated')
|
|
router.reload({ preserveScroll: true })
|
|
})
|
|
.catch(() => {
|
|
deletingAll.value = false
|
|
})
|
|
}
|
|
|
|
function downloadBundle() {
|
|
window.location.href = route('services.download-bundle', {
|
|
service: props.serviceId,
|
|
blockType: 'moderation',
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div data-testid="moderation-block" class="moderation-block space-y-4">
|
|
<!-- Block header with badge and delete-all (#2) -->
|
|
<div class="flex items-center justify-end gap-2">
|
|
<span
|
|
v-if="moderationSlides.length > 0"
|
|
class="inline-flex items-center gap-1 rounded-full bg-gray-100 px-2.5 py-1 text-xs font-semibold text-gray-700 ring-1 ring-gray-200/80"
|
|
>
|
|
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909M3.75 21h16.5a2.25 2.25 0 002.25-2.25V5.25a2.25 2.25 0 00-2.25-2.25H3.75A2.25 2.25 0 001.5 5.25v13.5A2.25 2.25 0 003.75 21z" />
|
|
</svg>
|
|
{{ moderationSlides.length }} {{ moderationSlides.length === 1 ? 'Folie' : 'Folien' }}
|
|
</span>
|
|
|
|
<!-- Delete all button (#2) -->
|
|
<button
|
|
v-if="moderationSlides.length > 0"
|
|
@click="confirmingDeleteAll = true"
|
|
class="flex h-7 w-7 items-center justify-center rounded-lg text-gray-400 transition hover:bg-red-50 hover:text-red-600"
|
|
title="Alle Folien löschen"
|
|
>
|
|
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<SlideGrid
|
|
data-testid="moderation-block-grid"
|
|
:slides="moderationSlides"
|
|
type="moderation"
|
|
:show-expire-date="false"
|
|
:show-uploader="true"
|
|
@deleted="handleSlideDeleted"
|
|
@updated="handleSlideUpdated"
|
|
>
|
|
<template #upload-card>
|
|
<SlideUploader
|
|
data-testid="moderation-block-uploader"
|
|
type="moderation"
|
|
:service-id="serviceId"
|
|
:show-expire-date="false"
|
|
:inline="true"
|
|
@uploaded="handleSlideUploaded"
|
|
/>
|
|
</template>
|
|
</SlideGrid>
|
|
|
|
<button
|
|
v-if="moderationSlides.length > 0"
|
|
@click="downloadBundle"
|
|
class="inline-flex items-center rounded-md border border-transparent bg-gray-600 px-4 py-2 text-xs font-semibold uppercase tracking-widest text-white transition duration-150 ease-in-out hover:bg-gray-700 focus:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 active:bg-gray-900"
|
|
data-testid="download-probundle-button-moderation"
|
|
>
|
|
<svg class="mr-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
</svg>
|
|
.probundle herunterladen
|
|
</button>
|
|
|
|
<!-- Delete all confirmation (#2) -->
|
|
<ConfirmDialog
|
|
:show="confirmingDeleteAll"
|
|
title="Alle Folien löschen?"
|
|
message="Möchtest du wirklich alle Moderationsfolien löschen?"
|
|
confirm-label="Alle löschen"
|
|
cancel-label="Abbrechen"
|
|
variant="danger"
|
|
@confirm="confirmDeleteAll"
|
|
@cancel="confirmingDeleteAll = false"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.moderation-block {
|
|
/* Component-specific styles if needed */
|
|
}
|
|
</style>
|