feat: registration field polish, multi-category tags, file uploads, Partner icon
- Restructure field editor dialog: move Options section to bottom with divider and subheader, fix delete button with flex layout - Change tag_category (single string) to tag_categories (JSON array) supporting multiple category selection in tag picker fields - Portal tag picker now groups tags by category with subheaders - Add generic file upload endpoint (FileUploadService + UploadController) - Replace email branding logo URL text field with ImageUploadField - Update Partner crowd type default icon to tabler-affiliate - Apply changes consistently to both field and template dialogs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
67
api/app/Services/FileUploadService.php
Normal file
67
api/app/Services/FileUploadService.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
final class FileUploadService
|
||||
{
|
||||
private const ALLOWED_IMAGE_MIMES = [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
'image/svg+xml',
|
||||
'image/gif',
|
||||
];
|
||||
|
||||
private const MAX_IMAGE_SIZE_KB = 5120; // 5MB
|
||||
|
||||
public function uploadImage(
|
||||
UploadedFile $file,
|
||||
string $directory,
|
||||
?string $organisationId = null,
|
||||
): string {
|
||||
$this->validateImage($file);
|
||||
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
$filename = Str::ulid() . '.' . $extension;
|
||||
$path = trim($directory, '/');
|
||||
|
||||
if ($organisationId) {
|
||||
$path .= '/' . $organisationId;
|
||||
}
|
||||
|
||||
Storage::disk('public')->putFileAs($path, $file, $filename);
|
||||
|
||||
return Storage::disk('public')->url($path . '/' . $filename);
|
||||
}
|
||||
|
||||
public function deleteByUrl(string $url): void
|
||||
{
|
||||
$baseUrl = Storage::disk('public')->url('');
|
||||
$path = str_replace($baseUrl, '', $url);
|
||||
|
||||
if ($path && Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
|
||||
private function validateImage(UploadedFile $file): void
|
||||
{
|
||||
if (!in_array($file->getMimeType(), self::ALLOWED_IMAGE_MIMES, true)) {
|
||||
throw new \DomainException(
|
||||
'Alleen JPG, PNG, WebP, SVG of GIF bestanden zijn toegestaan.'
|
||||
);
|
||||
}
|
||||
|
||||
if ($file->getSize() / 1024 > self::MAX_IMAGE_SIZE_KB) {
|
||||
throw new \DomainException(
|
||||
'Bestand is te groot. Maximum: 5MB.'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ final class RegistrationFieldTemplateService
|
||||
'slug' => $slug,
|
||||
'field_type' => $template->field_type,
|
||||
'options' => $template->options,
|
||||
'tag_category' => $template->tag_category,
|
||||
'tag_categories' => $template->tag_categories,
|
||||
'is_required' => $template->is_required,
|
||||
'is_portal_visible' => $template->is_portal_visible,
|
||||
'is_admin_only' => $template->is_admin_only,
|
||||
@@ -155,7 +155,7 @@ final class RegistrationFieldTemplateService
|
||||
['label' => 'EHBO / BHV diploma', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 10],
|
||||
['label' => 'Rijbewijs', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 11],
|
||||
['label' => 'Eerder vrijwilliger geweest', 'field_type' => 'boolean', 'is_filterable' => true, 'display_width' => 'half', 'sort_order' => 12],
|
||||
['label' => 'Certificaten & vaardigheden', 'field_type' => 'tag_picker', 'tag_category' => null, 'is_filterable' => true, 'display_width' => 'full', 'sort_order' => 13],
|
||||
['label' => 'Certificaten & vaardigheden', 'field_type' => 'tag_picker', 'tag_categories' => null, 'is_filterable' => true, 'display_width' => 'full', 'sort_order' => 13],
|
||||
['label' => 'Aanvullende informatie', 'field_type' => 'heading', 'display_width' => 'full', 'sort_order' => 14],
|
||||
['label' => 'Toestemming gegevensverwerking', 'field_type' => 'boolean', 'is_required' => true, 'help_text' => 'Ik geef toestemming voor de verwerking van mijn persoonsgegevens ten behoeve van de organisatie van dit evenement, conform de Algemene Verordening Gegevensbescherming (AVG).', 'display_width' => 'full', 'sort_order' => 15],
|
||||
['label' => 'Opmerkingen', 'field_type' => 'textarea', 'display_width' => 'full', 'sort_order' => 16],
|
||||
|
||||
@@ -171,7 +171,7 @@ final class RegistrationFormFieldService
|
||||
'slug' => $slug,
|
||||
'field_type' => $sourceField->field_type,
|
||||
'options' => $sourceField->options,
|
||||
'tag_category' => $sourceField->tag_category,
|
||||
'tag_categories' => $sourceField->tag_categories,
|
||||
'is_required' => $sourceField->is_required,
|
||||
'is_portal_visible' => $sourceField->is_portal_visible,
|
||||
'is_admin_only' => $sourceField->is_admin_only,
|
||||
|
||||
Reference in New Issue
Block a user