feat(ui): add slide upload on agenda items
This commit is contained in:
parent
f78d20fc59
commit
45955b70a2
|
|
@ -21,13 +21,14 @@ public function store(Request $request, FileConversionService $conversionService
|
|||
{
|
||||
$validated = $request->validate([
|
||||
'file' => ['required', 'file', 'max:51200'],
|
||||
'type' => ['required', Rule::in(['information', 'moderation', 'sermon'])],
|
||||
'type' => ['required', Rule::in(['information', 'moderation', 'sermon', 'agenda_item'])],
|
||||
'service_id' => ['nullable', 'exists:services,id'],
|
||||
'service_agenda_item_id' => ['nullable', 'integer', 'exists:service_agenda_items,id'],
|
||||
'expire_date' => ['nullable', 'date'],
|
||||
]);
|
||||
|
||||
// moderation and sermon slides require a service_id
|
||||
if (in_array($validated['type'], ['moderation', 'sermon']) && empty($validated['service_id'])) {
|
||||
// moderation, sermon, and agenda_item slides require a service_id
|
||||
if (in_array($validated['type'], ['moderation', 'sermon', 'agenda_item']) && empty($validated['service_id'])) {
|
||||
return response()->json([
|
||||
'message' => 'Moderations- und Predigtfolien benötigen einen Gottesdienst.',
|
||||
'errors' => ['service_id' => ['Gottesdienst ist erforderlich.']],
|
||||
|
|
@ -49,21 +50,22 @@ public function store(Request $request, FileConversionService $conversionService
|
|||
|
||||
$uploaderName = $request->user()?->name ?? 'Unbekannt';
|
||||
$serviceId = $validated['service_id'] ?? null;
|
||||
$serviceAgendaItemId = $validated['service_agenda_item_id'] ?? null;
|
||||
$type = $validated['type'];
|
||||
$expireDate = $validated['expire_date'] ?? null;
|
||||
|
||||
// Handle PowerPoint files — dispatch async job
|
||||
if (in_array($extension, self::POWERPOINT_EXTENSIONS, true)) {
|
||||
return $this->handlePowerPoint($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate);
|
||||
return $this->handlePowerPoint($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate, $serviceAgendaItemId);
|
||||
}
|
||||
|
||||
// Handle ZIP files — extract and process
|
||||
if ($extension === 'zip') {
|
||||
return $this->handleZip($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate);
|
||||
return $this->handleZip($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate, $serviceAgendaItemId);
|
||||
}
|
||||
|
||||
// Handle images — convert synchronously
|
||||
return $this->handleImage($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate);
|
||||
return $this->handleImage($file, $conversionService, $type, $serviceId, $uploaderName, $expireDate, $serviceAgendaItemId);
|
||||
}
|
||||
|
||||
public function destroy(Slide $slide): JsonResponse
|
||||
|
|
@ -79,7 +81,7 @@ public function destroy(Slide $slide): JsonResponse
|
|||
public function destroyBulk(Request $request): JsonResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'type' => ['required', Rule::in(['information', 'moderation', 'sermon'])],
|
||||
'type' => ['required', Rule::in(['information', 'moderation', 'sermon', 'agenda_item'])],
|
||||
'service_id' => ['nullable', 'exists:services,id'],
|
||||
]);
|
||||
|
||||
|
|
@ -155,6 +157,7 @@ private function handleImage(
|
|||
?int $serviceId,
|
||||
string $uploaderName,
|
||||
?string $expireDate,
|
||||
?int $serviceAgendaItemId = null,
|
||||
): JsonResponse {
|
||||
try {
|
||||
$result = $conversionService->convertImage($file);
|
||||
|
|
@ -162,6 +165,7 @@ private function handleImage(
|
|||
$slide = Slide::create([
|
||||
'type' => $type,
|
||||
'service_id' => $serviceId,
|
||||
'service_agenda_item_id' => $serviceAgendaItemId,
|
||||
'original_filename' => $file->getClientOriginalName(),
|
||||
'stored_filename' => $result['filename'],
|
||||
'thumbnail_filename' => $result['thumbnail'],
|
||||
|
|
@ -190,6 +194,7 @@ private function handlePowerPoint(
|
|||
?int $serviceId,
|
||||
string $uploaderName,
|
||||
?string $expireDate,
|
||||
?int $serviceAgendaItemId = null,
|
||||
): JsonResponse {
|
||||
// Store file persistently so the job can access it
|
||||
$storedPath = $file->store('temp/ppt', 'local');
|
||||
|
|
@ -206,6 +211,7 @@ private function handlePowerPoint(
|
|||
'meta' => [
|
||||
'type' => $type,
|
||||
'service_id' => $serviceId,
|
||||
'service_agenda_item_id' => $serviceAgendaItemId,
|
||||
'uploader_name' => $uploaderName,
|
||||
'expire_date' => $expireDate,
|
||||
'original_filename' => $file->getClientOriginalName(),
|
||||
|
|
@ -226,6 +232,7 @@ private function handleZip(
|
|||
?int $serviceId,
|
||||
string $uploaderName,
|
||||
?string $expireDate,
|
||||
?int $serviceAgendaItemId = null,
|
||||
): JsonResponse {
|
||||
try {
|
||||
$results = $conversionService->processZip($file);
|
||||
|
|
@ -242,6 +249,7 @@ private function handleZip(
|
|||
$slides[] = Slide::create([
|
||||
'type' => $type,
|
||||
'service_id' => $serviceId,
|
||||
'service_agenda_item_id' => $serviceAgendaItemId,
|
||||
'original_filename' => $file->getClientOriginalName(),
|
||||
'stored_filename' => $result['filename'],
|
||||
'thumbnail_filename' => $result['thumbnail'],
|
||||
|
|
|
|||
|
|
@ -92,8 +92,9 @@ function onUploaded() {
|
|||
<!-- Folien-Upload (umschaltbar) -->
|
||||
<div v-if="showUploader" class="mt-3 border-t pt-3">
|
||||
<SlideUploader
|
||||
type="sermon"
|
||||
type="agenda_item"
|
||||
:service-id="serviceId"
|
||||
:agenda-item-id="agendaItem.id"
|
||||
:show-expire-date="false"
|
||||
:inline="true"
|
||||
@uploaded="onUploaded"
|
||||
|
|
|
|||
|
|
@ -9,12 +9,16 @@ const props = defineProps({
|
|||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (v) => ['information', 'moderation', 'sermon'].includes(v),
|
||||
validator: (v) => ['information', 'moderation', 'sermon', 'agenda_item'].includes(v),
|
||||
},
|
||||
serviceId: {
|
||||
type: [Number, null],
|
||||
default: null,
|
||||
},
|
||||
agendaItemId: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
showExpireDate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
|
@ -107,6 +111,10 @@ function uploadNextFile(index) {
|
|||
formData.append('service_id', props.serviceId)
|
||||
}
|
||||
|
||||
if (props.agendaItemId) {
|
||||
formData.append('service_agenda_item_id', props.agendaItemId)
|
||||
}
|
||||
|
||||
if (props.showExpireDate && expireDate.value) {
|
||||
formData.append('expire_date', expireDate.value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceAgendaItem;
|
||||
use App\Models\Slide;
|
||||
use App\Models\User;
|
||||
use App\Services\FileConversionService;
|
||||
|
|
@ -149,7 +150,7 @@
|
|||
|
||||
// Build zip
|
||||
$zipPath = $tempDir.'/slides.zip';
|
||||
$zip = new ZipArchive;
|
||||
$zip = new ZipArchive();
|
||||
$zip->open($zipPath, ZipArchive::CREATE);
|
||||
$zip->addFile($imgPath, 'slide1.png');
|
||||
$zip->close();
|
||||
|
|
@ -171,6 +172,51 @@
|
|||
@rmdir($tempDir);
|
||||
});
|
||||
|
||||
test('upload agenda_item slide with service_agenda_item_id links to agenda item', function () {
|
||||
$service = Service::factory()->create();
|
||||
$agendaItem = ServiceAgendaItem::factory()->create(['service_id' => $service->id]);
|
||||
$file = makePngUploadForSlide('agenda-slide.png', 800, 600);
|
||||
|
||||
$response = $this->post(route('slides.store'), [
|
||||
'file' => $file,
|
||||
'type' => 'agenda_item',
|
||||
'service_id' => $service->id,
|
||||
'service_agenda_item_id' => $agendaItem->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['success' => true]);
|
||||
|
||||
$slide = Slide::first();
|
||||
expect($slide)->not->toBeNull();
|
||||
expect($slide->type)->toBe('agenda_item');
|
||||
expect($slide->service_id)->toBe($service->id);
|
||||
expect($slide->service_agenda_item_id)->toBe($agendaItem->id);
|
||||
});
|
||||
|
||||
test('upload agenda_item slide without service_id fails', function () {
|
||||
$file = makePngUploadForSlide('agenda-slide.png', 400, 300);
|
||||
|
||||
$response = $this->post(route('slides.store'), [
|
||||
'file' => $file,
|
||||
'type' => 'agenda_item',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
});
|
||||
|
||||
test('agenda_item type is accepted in validation', function () {
|
||||
$file = makePngUploadForSlide('test.png', 400, 300);
|
||||
|
||||
$response = $this->postJson(route('slides.store'), [
|
||||
'file' => $file,
|
||||
'type' => 'agenda_item',
|
||||
'service_id' => Service::factory()->create()->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
});
|
||||
|
||||
test('unauthenticated user cannot upload slides', function () {
|
||||
auth()->logout();
|
||||
$file = makePngUploadForSlide('test.png', 400, 300);
|
||||
|
|
|
|||
Loading…
Reference in a new issue