@php $shareUrl = $event->share_url; $chunkSize = $chunkSize ?? (int) config('events.media.chunk_size', 5242880); $ftpBasePath = $ftpBasePath ?? rtrim((string) config('events.media.ftp_path'), DIRECTORY_SEPARATOR); @endphp

{{ $event->name }}

{{ __('Photos:') }} {{ $mediaCounts['total'] }} · {{ __('Date:') }} {{ \App\Support\FormatSettings::date($event->event_date) }}

{{ $event->is_active ? __('Published') : __('Inactive') }} {{ __('Share') }}
@if(app(\App\Services\PlanLimitService::class)->hasFeature(auth()->user(), 'has_guest_upload')) @endif @if (auth()->user()->hasRole(['Super Admin', 'Admin']) || (auth()->user()->plan && auth()->user()->plan->has_design_editor)) {{ __('Design') }} @endif

{{ __('Media Uploads') }}

{{ __('Upload images or videos, then monitor processing status.') }}

{{ __('Images:') }} {{ $mediaCounts['images'] }} {{ __('Videos:') }} {{ $mediaCounts['videos'] }}
@csrf

{{ __('Upload Files') }}

{{ __('Best for small batches (up to 50 files).') }}

@csrf

{{ __('ZIP Upload') }} {{ __('Recommended for bulk') }}

{{ __('Upload 100-5000+ photos in one go. Just zip your folder and upload. Much faster than individual files.') }}

@if(app(\App\Services\PlanLimitService::class)->hasFeature(auth()->user(), 'has_google_drive_import'))

{{ __('Google Drive Import') }}

{{ __('Paste a public folder link to import images and MP4s.') }}

@endif @if(app(\App\Services\PlanLimitService::class)->hasFeature(auth()->user(), 'has_google_drive_import')) @if(($driveImports ?? collect())->isNotEmpty())

{{ __('Google Drive Import Progress') }}

{{ __('Auto-refreshes while this page is open.') }}

@foreach ($driveImports as $import) @php $total = $import->total_files ?? 0; $done = $import->processed_files ?? 0; $failed = $import->failed_files ?? 0; $remaining = max(0, $total - $done - $failed); $percent = $total > 0 ? (int) round(($done / $total) * 100) : 0; @endphp
#{{ $import->id }} {{ ucfirst($import->status) }}
{{ $done }} / {{ $total }} {{ __('processed') }} · {{ $failed }} {{ __('failed') }}
{{ __('Remaining:') }} {{ $remaining }} @if($import->last_error) {{ $import->last_error }} @endif
@endforeach
@endif
@csrf

{{ __('Import from Google Drive') }}

{{ __('Use a public Google Drive folder link. Supported types: JPG, JPEG, PNG, WEBP, MP4.') }}

{{ __('Cancel') }} {{ __('Import') }}
@endif
{{ __('Reset') }}
@php $exportQuery = array_merge(request()->query(), ['format' => 'csv']); @endphp
{{ __('Invoice') }}
@forelse ($media as $item) @php $previewUrl = $item->thumbnail_url ?: $item->preview_url; $sizeLabel = $item->size ? number_format($item->size / 1024, 1) . ' KB' : 'N/A'; @endphp @empty @endforelse
{{ __('Preview') }} {{ __('Type') }} {{ __('Size') }} {{ __('Status') }} {{ __('Actions') }}
@if ($item->file_type === 'video') @else @endif {{ strtoupper($item->file_type) }} {{ $sizeLabel }} {{ ucfirst($item->status) }}
{{ __('Download') }} {{ __('Original') }} @if ($item->file_type === 'image')
@csrf
@endif
@csrf
@csrf @method('DELETE')

{{ __('Delete this file?') }}

{!! __('This file will be permanently deleted from the server. This action cannot be undone.') !!}

{{ __('Cancel') }} {{ __('Delete Permanently') }}

{{ strtoupper($item->file_type) }} · {{ $sizeLabel }}

{{ __('Close') }}
@if ($item->file_type === 'video') @else {{ $item->file_name }} @endif
{{ __('No media uploaded yet.') }}
@forelse ($media as $item) @php $previewUrl = $item->thumbnail_url ?: $item->preview_url; $sizeLabel = $item->size ? number_format($item->size / 1024, 1) . ' KB' : 'N/A'; @endphp
@if ($item->file_type === 'video') @else {{ $item->file_name }} @endif
{{ $item->file_type }} · {{ $sizeLabel }}
{{ ucfirst($item->status) }} @if ($item->is_cover) {{ __('Cover') }} @endif @if ($item->is_featured) {{ __('Featured') }} @endif
{{ __('Original') }} @if ($item->file_type === 'image')
@csrf
@endif
@csrf
@csrf @method('DELETE')

{{ __('Delete') }} {{ $item->file_name }}?

{!! __('This file will be permanently deleted from the server. This action cannot be undone.') !!}

{{ __('Cancel') }} {{ __('Delete Permanently') }}
@empty

{{ __('No photos uploaded yet.') }}

@endforelse
{{ $media->links() }}
@csrf

{{ __('Download selected media?') }}

{!! __('You are about to download 0 item(s).') !!}

{{ __('Cancel') }} {{ __('Download') }}
@csrf @method('DELETE')

{{ __('Delete selected media?') }}

{!! __('You are about to permanently delete 0 file(s) from the server. All photos/videos will be removed and cannot be recovered.') !!}

{{ __('Cancel') }} {{ __('Delete Permanently') }}
@if(app(\App\Services\PlanLimitService::class)->hasFeature(auth()->user(), 'has_guest_upload'))

{{ __('Guest Uploads') }} {{ $event->allow_guest_upload ? __('Active') : __('Disabled') }}

{{ __('Manage media uploaded by guests via the shared link.') }}

@if(!$event->allow_guest_upload) {{ __('Enable Guest Uploads') }} @endif
@if(!$event->allow_guest_upload)

{{ __('Guest Uploads Disabled') }}

{{ __('Enable this feature in event settings to allow guests to upload their own photos.') }}

@else
@forelse ($media->where('is_guest_upload', true) as $item) @php $previewUrl = $item->thumbnail_url ?: $item->preview_url; @endphp
{{ $item->file_name }}

{{ $item->file_name }}

@csrf @method('DELETE')

{{ __('Delete') }} {{ $item->file_name }}?

{!! __('This file will be permanently deleted from the server. This action cannot be undone.') !!}

{{ __('Cancel') }} {{ __('Delete Permanently') }}
@empty

{{ __('No guest uploads found.') }}

@endforelse
@endif @else

{{ __('Guest Uploads Not Available') }}

{{ __('Your current plan does not include guest uploads. Please upgrade to enable this feature.') }}

@endif

{{ __('Event') }}

{{ $event->name }}

{{ __('Type:') }} {{ $event->type }}

{{ __('Event Date:') }} {{ \App\Support\FormatSettings::date($event->event_date) }}

{{ __('Expiry') }}

{{ $event->expiry_date ? \App\Support\FormatSettings::date($event->expiry_date) : __('Not set') }}

{{ __('Notifications') }}

{{ $event->notifications_enabled ? __('Enabled') : __('Disabled') }}

{{ __('Status') }}

{{ $event->is_active ? __('Active') : __('Inactive') }}

{{ __('Media') }}

{{ $mediaCounts['total'] }} {{ __('total') }}

@if ($event->details)

{{ __('Details') }}

{{ $event->details }}

@endif

{{ __('Queue') }}

{{ $mediaPipeline['queue_total'] }} {{ __('items') }}

{{ __('Pending') }} {{ $mediaPipeline['pending'] }} · {{ __('Processing') }} {{ $mediaPipeline['processing'] }}

{{ __('Ready') }}

{{ $mediaPipeline['ready'] }} {{ __('items') }}

{{ __('Failed') }}

{{ $mediaPipeline['failed'] }} {{ __('items') }}

{{ __('Storage Used') }}

{{ $mediaPipeline['size_label'] }}

{{ __('Share URL') }}

{{ __('QR Code') }}

{{ __('QR code') }}

{{ __('Guest Pin') }}

{{ $event->guest_pin ? __('Set') : __('Not set') }}

{{ __('Admin Pin') }}

{{ $event->admin_pin ? __('Set') : __('Not set') }}

{{ __('Hash Tags') }}

@forelse ($event->hashtags ?? [] as $tag) {{ $tag }} @empty {{ __('No tags set.') }} @endforelse
@if(app(\App\Services\PlanLimitService::class)->hasFeature(auth()->user(), 'has_ftp_import'))

{{ __('FTP Tethering') }}

{{ $ftpBasePath }}/{{ $event->id }}

{{ __('Photos uploaded to this folder are automatically imported every 2 minutes.') }}

@csrf

{{ __('How FTP Camera Tethering Works') }}

{{ __('Shoot photos and have them appear in your event gallery automatically, no manual uploading needed.') }}

1

{{ __('Connect your camera to the same Wi-Fi as your server') }}

{{ __("Your camera and your hosting server must be on the same network. If you're at a venue, connect both to the venue's Wi-Fi or use a portable router / mobile hotspot.") }}

2

{{ __("Open your camera's FTP settings") }}

{!! __('Go to your camera\'s Wi-Fi / Network menu and look for "FTP Transfer", "Wireless Transmitter", or "Auto Send". This is available on most professional Canon, Nikon, and Sony cameras.') !!}

Canon: Menu → Communication → Built-in Wireless → FTP Transfer

Nikon: Menu → Network → Connect to FTP Server

Sony: Menu → Network → FTP Transfer Function

3

{{ __("Enter your server's FTP details") }}

{{ __("Fill in the following in your camera's FTP configuration:") }}

{{ __('Server / Host') }} {{ __('Your server IP or domain') }}
{{ __('Port') }} {{ __('21 (default)') }}
{{ __('Username') }} {{ __('Your FTP username') }}
{{ __('Password') }} {{ __('Your FTP password') }}
{{ __('Directory') }} {{ $ftpBasePath }}/{{ $event->id }}

{{ __('You can find your FTP credentials in your hosting control panel (cPanel, Plesk, etc).') }}

4

{{ __('Start shooting') }}

{!! __('Every photo you take will be sent to the server automatically over Wi-Fi. Supported file types: JPG, JPEG, PNG, WEBP, and MP4 videos.') !!}

5

{{ __('Photos appear in your gallery automatically') }}

{!! __('The system checks for new FTP photos every 2 minutes and adds them to this event\'s gallery automatically. Imported files are moved to a processed/ subfolder so they won\'t be imported twice.') !!}

{!! __('You can also click "Ingest FTP Now" to import immediately without waiting.') !!}

{{ __('Tips for best results') }}

  • {!! __('Use a dedicated Wi-Fi router at the venue for stable, fast transfers.') !!}
  • {!! __("If your camera doesn't have built-in FTP, use an app like ShutterSnitch (iOS) or CamFi on a laptop as an FTP bridge.") !!}
  • {!! __('For multiple cameras, point them all to the same event folder — they\'ll all feed into the same gallery.') !!}
  • {{ __('Guests will see photos appear on the event share page in real time.') }}
{{ __('Got it') }}
@endif
@php $allSelections = \App\Models\ClientSelection::where('event_id', $event->id) ->with('media:id,file_name,optimized_path,original_path,disk,meta') ->orderByDesc('created_at') ->limit(200) ->get(); $selectionsByGuest = $allSelections->groupBy(fn($s) => $s->session_id); $totalUnique = $allSelections->unique('event_media_id')->count(); @endphp @if($allSelections->isEmpty())

{{ __('No client selections yet.') }}

{{ __('Guests can select their favorite photos on the share page using the heart button.') }}

@else

{{ __('Client Selections') }}

{{ $totalUnique }} {{ __('unique photo(s) from') }} {{ $selectionsByGuest->count() }} {{ __('guest(s)') }}
@foreach($selectionsByGuest as $sessionId => $guestSelections) @php $first = $guestSelections->first(); @endphp
{{ $first->guest_name ?: __('Guest') . ' #' . Str::limit($sessionId, 8) }} @if($first->guest_email) ({{ $first->guest_email }}) @endif
{{ $guestSelections->count() }} {{ __('photos') }}
@foreach($guestSelections as $sel) @if($sel->media)
{{ $sel->media->file_name }}
@endif @endforeach
@endforeach @endif
@php $analytics = \App\Models\GalleryAnalytics::where('event_id', $event->id) ->orderByDesc('date') ->limit(30) ->get(); $totals = [ 'views' => $analytics->sum('views'), 'unique_visitors' => $analytics->sum('unique_visitors'), 'downloads' => $analytics->sum('downloads'), 'face_searches' => $analytics->sum('face_searches'), 'selections' => $analytics->sum('selections'), ]; $topDownloaded = \App\Models\MediaAnalytics::whereHas('media', fn($q) => $q->where('event_id', $event->id)) ->orderByDesc('downloads') ->with('media:id,file_name,optimized_path,original_path,disk,meta') ->limit(5) ->get(); @endphp
@foreach([ 'views' => [__('Total Views'), 'text-brand'], 'unique_visitors' => [__('Unique Visitors'), 'text-purple-500'], 'downloads' => [__('Downloads'), 'text-green-500'], 'face_searches' => [__('Face Searches'), 'text-blue-500'], 'selections' => [__('Selections'), 'text-pink-500'], ] as $key => [$label, $color])
{{ number_format($totals[$key]) }}
{{ $label }}
@endforeach
@if($topDownloaded->isNotEmpty())

{{ __('Most Downloaded') }}

@foreach($topDownloaded as $i => $td)
{{ $i + 1 }}. @if($td->media)
{{ $td->media->file_name }} @endif {{ $td->downloads }} {{ __('downloads') }}
@endforeach
@endif @if($analytics->isEmpty())

{{ __('No analytics data yet. Share the gallery to start tracking.') }}

@endif

{{ __('General Settings') }}

@csrf @method('PUT')

{{ __('If enabled, everyone can see all photos and download them directly without a face search/selfie requirement.') }}

allow_public_downloads ? 'checked' : '' }}>
@if(app(\App\Services\PlanLimitService::class)->hasFeature(auth()->user(), 'has_guest_upload'))

{{ __('Allow guests to upload their own media to this event.') }}

allow_guest_upload ? 'checked' : '' }}>
@endif

{{ __("If inactive, the gallery links will show an 'inactive' message.") }}

is_active ? 'checked' : '' }}>
{{-- Upload How it works modal --}}

{{ __('How Photo Uploading Works') }}

{{ __("Upload photos and they'll be processed automatically in the background.") }}

1

{{ __('Choose your upload method') }}

{!! __('Upload Files — Best for small batches (up to 50 files). Select multiple images or videos at once.') !!}

{!! __('ZIP Upload — Recommended for bulk (100-5000+ photos). Zip your folder and upload it in one go. Much faster than individual files.') !!}

2

{{ __('Files upload instantly') }}

{!! __("Your photos are uploaded to the server immediately. You'll see a progress bar during upload. Keep the tab open until uploading completes.") !!}

3

{{ __('Background processing begins') }}

{{ __('After upload, each photo is optimized in the background — resizing, generating thumbnails, and applying watermarks if enabled.') }}

@if($watermarkEnabled)

{!! __('Watermark ON: ~1-2 sec per photo') !!}

{{ __('100 photos') }} ≈ {{ __('2 min') }} · {{ __('1000 photos') }} ≈ {{ __('20 min') }}

@else

{!! __('Watermark OFF: Near-instant processing') !!}

@endif
4

{{ __('Page reloads when ready') }}

{!! __('A processing banner will appear showing progress. Once all photos are optimized and active, the page reloads automatically so you can see the final results.') !!}

{{ __('Tips for best results') }}

  • {!! __('Use JPG, JPEG, PNG, WEBP for images and MP4 for videos.') !!}
  • {!! __('For large batches (100+ files), always use ZIP Upload for the fastest experience.') !!}
  • {!! __('Enable Chunked mode for individual files larger than 50 MB.') !!}
  • {{ __('Guests will see photos appear on the event share page once processing is complete.') }}
{{ __('Got it') }}
{{-- Processing overlay (hidden by default, shown after upload via JS) --}}
@push('scripts') {{-- Auto queue processor: processes pending media jobs via AJAX, no cron needed --}} @endpush