feat: extend event toolkit and polish guest pwa

This commit is contained in:
Codex Agent
2025-10-28 18:28:22 +01:00
parent f29067f570
commit a7bbf230fd
45 changed files with 3809 additions and 351 deletions

View File

@@ -4,7 +4,9 @@ namespace App\Http\Controllers\Api\Tenant;
use App\Http\Controllers\Controller;
use App\Http\Requests\Tenant\EventStoreRequest;
use App\Http\Resources\Tenant\EventJoinTokenResource;
use App\Http\Resources\Tenant\EventResource;
use App\Http\Resources\Tenant\PhotoResource;
use App\Models\Event;
use App\Models\EventPackage;
use App\Models\Package;
@@ -228,6 +230,10 @@ class EventController extends Controller
unset($validated[$unused]);
}
if (isset($validated['settings']) && is_array($validated['settings'])) {
$validated['settings'] = array_merge($event->settings ?? [], $validated['settings']);
}
$event->update($validated);
$event->load(['eventType', 'tenant']);
@@ -277,6 +283,141 @@ class EventController extends Controller
]);
}
public function toolkit(Request $request, Event $event): JsonResponse
{
$tenantId = $request->attributes->get('tenant_id');
if ($event->tenant_id !== $tenantId) {
return response()->json(['error' => 'Event not found'], 404);
}
$event->load(['eventType', 'eventPackage.package']);
$photoQuery = Photo::query()->where('event_id', $event->id);
$pendingPhotos = (clone $photoQuery)
->where('status', 'pending')
->latest('created_at')
->take(6)
->get();
$recentUploads = (clone $photoQuery)
->where('status', 'approved')
->latest('created_at')
->take(8)
->get();
$pendingCount = (clone $photoQuery)->where('status', 'pending')->count();
$uploads24h = (clone $photoQuery)->where('created_at', '>=', now()->subDay())->count();
$totalUploads = (clone $photoQuery)->count();
$tasks = $event->tasks()
->orderBy('tasks.sort_order')
->orderBy('tasks.created_at')
->get(['tasks.id', 'tasks.title', 'tasks.description', 'tasks.priority', 'tasks.is_completed']);
$taskSummary = [
'total' => $tasks->count(),
'completed' => $tasks->where('is_completed', true)->count(),
];
$taskSummary['pending'] = max(0, $taskSummary['total'] - $taskSummary['completed']);
$translate = static function ($value, string $fallback = '') {
if (is_array($value)) {
$locale = app()->getLocale();
$candidates = array_filter([
$locale,
$locale && str_contains($locale, '-') ? explode('-', $locale)[0] : null,
'de',
'en',
]);
foreach ($candidates as $candidate) {
if ($candidate && isset($value[$candidate]) && $value[$candidate] !== '') {
return $value[$candidate];
}
}
$first = reset($value);
return $first !== false ? $first : $fallback;
}
if (is_string($value) && $value !== '') {
return $value;
}
return $fallback;
};
$taskPreview = $tasks
->take(6)
->map(fn ($task) => [
'id' => $task->id,
'title' => $translate($task->title, 'Task'),
'description' => $translate($task->description, null),
'is_completed' => (bool) $task->is_completed,
'priority' => $task->priority,
])
->values();
$joinTokenQuery = $event->joinTokens();
$totalInvites = (clone $joinTokenQuery)->count();
$activeInvites = (clone $joinTokenQuery)
->whereNull('revoked_at')
->where(function ($query) {
$query->whereNull('expires_at')
->orWhere('expires_at', '>', now());
})
->where(function ($query) {
$query->whereNull('usage_limit')
->orWhereColumn('usage_limit', '>', 'usage_count');
})
->count();
$recentInvites = (clone $joinTokenQuery)
->orderByDesc('created_at')
->take(3)
->get();
$alerts = [];
if (($event->settings['engagement_mode'] ?? 'tasks') !== 'photo_only' && $taskSummary['total'] === 0) {
$alerts[] = 'no_tasks';
}
if ($activeInvites === 0) {
$alerts[] = 'no_invites';
}
if ($pendingCount > 0) {
$alerts[] = 'pending_photos';
}
return response()->json([
'event' => new EventResource($event),
'metrics' => [
'uploads_total' => $totalUploads,
'uploads_24h' => $uploads24h,
'pending_photos' => $pendingCount,
'active_invites' => $activeInvites,
'engagement_mode' => $event->settings['engagement_mode'] ?? 'tasks',
],
'tasks' => [
'summary' => $taskSummary,
'items' => $taskPreview,
],
'photos' => [
'pending' => PhotoResource::collection($pendingPhotos)->resolve($request),
'recent' => PhotoResource::collection($recentUploads)->resolve($request),
],
'invites' => [
'summary' => [
'total' => $totalInvites,
'active' => $activeInvites,
],
'items' => EventJoinTokenResource::collection($recentInvites)->resolve($request),
],
'alerts' => $alerts,
]);
}
public function toggle(Request $request, Event $event): JsonResponse
{
$tenantId = $request->attributes->get('tenant_id');