feat: implement tenant OAuth flow and guest achievements
This commit is contained in:
@@ -279,7 +279,6 @@ class EventPublicController extends BaseController
|
||||
'photos.likes_count',
|
||||
'photos.emotion_id',
|
||||
'photos.task_id',
|
||||
'photos.created_at',
|
||||
'photos.guest_name',
|
||||
'tasks.title as task_title'
|
||||
])
|
||||
@@ -503,4 +502,220 @@ class EventPublicController extends BaseController
|
||||
|
||||
return $defaultEmotion ?: 1; // Ultimate fallback to emotion ID 1 (assuming "Happy" exists)
|
||||
}
|
||||
public function achievements(Request $request, string $slug)
|
||||
{
|
||||
$event = DB::table('events')->where('slug', $slug)->where('status', 'published')->first(['id']);
|
||||
if (! $event) {
|
||||
return response()->json(['error' => ['code' => 'not_found', 'message' => 'Event not found or not public']], 404);
|
||||
}
|
||||
|
||||
$eventId = $event->id;
|
||||
$locale = $request->query('locale', 'de');
|
||||
|
||||
$totalPhotos = (int) DB::table('photos')->where('event_id', $eventId)->count();
|
||||
$uniqueGuests = (int) DB::table('photos')->where('event_id', $eventId)->distinct('guest_name')->count('guest_name');
|
||||
$tasksSolved = (int) DB::table('photos')->where('event_id', $eventId)->whereNotNull('task_id')->count();
|
||||
$likesTotal = (int) DB::table('photos')->where('event_id', $eventId)->sum('likes_count');
|
||||
|
||||
$summary = [
|
||||
'total_photos' => $totalPhotos,
|
||||
'unique_guests' => $uniqueGuests,
|
||||
'tasks_solved' => $tasksSolved,
|
||||
'likes_total' => $likesTotal,
|
||||
];
|
||||
|
||||
$guestNameParam = trim((string) $request->query('guest_name', ''));
|
||||
$deviceIdHeader = (string) $request->headers->get('X-Device-Id', '');
|
||||
$deviceId = substr(preg_replace('/[^A-Za-z0-9 _\-]/', '', $deviceIdHeader), 0, 120);
|
||||
$candidate = $guestNameParam !== '' ? $guestNameParam : $deviceId;
|
||||
$guestIdentifier = $candidate !== '' ? substr(preg_replace('/[^A-Za-z0-9 _\-]/', '', $candidate), 0, 120) : null;
|
||||
|
||||
$personal = null;
|
||||
if ($guestIdentifier) {
|
||||
$personalPhotos = (int) DB::table('photos')
|
||||
->where('event_id', $eventId)
|
||||
->where('guest_name', $guestIdentifier)
|
||||
->count();
|
||||
|
||||
$personalTasks = (int) DB::table('photos')
|
||||
->where('event_id', $eventId)
|
||||
->where('guest_name', $guestIdentifier)
|
||||
->whereNotNull('task_id')
|
||||
->distinct('task_id')
|
||||
->count('task_id');
|
||||
|
||||
$personalLikes = (int) DB::table('photos')
|
||||
->where('event_id', $eventId)
|
||||
->where('guest_name', $guestIdentifier)
|
||||
->sum('likes_count');
|
||||
|
||||
$badgeBlueprints = [
|
||||
['id' => 'first_photo', 'title' => 'Erstes Foto', 'description' => 'Lade dein erstes Foto hoch.', 'target' => 1, 'metric' => 'photos'],
|
||||
['id' => 'five_photos', 'title' => 'Fotoflair', 'description' => 'Fuenf Fotos hochgeladen.', 'target' => 5, 'metric' => 'photos'],
|
||||
['id' => 'first_task', 'title' => 'Aufgabenstarter', 'description' => 'Erste Aufgabe erfuellt.', 'target' => 1, 'metric' => 'tasks'],
|
||||
['id' => 'task_master', 'title' => 'Aufgabenmeister', 'description' => 'Fuenf Aufgaben erfuellt.', 'target' => 5, 'metric' => 'tasks'],
|
||||
['id' => 'crowd_pleaser', 'title' => 'Publikumsliebling', 'description' => 'Sammle zwanzig Likes.', 'target' => 20, 'metric' => 'likes'],
|
||||
];
|
||||
|
||||
$personalBadges = [];
|
||||
foreach ($badgeBlueprints as $badge) {
|
||||
$value = 0;
|
||||
if ($badge['metric'] === 'photos') {
|
||||
$value = $personalPhotos;
|
||||
} elseif ($badge['metric'] === 'tasks') {
|
||||
$value = $personalTasks;
|
||||
} else {
|
||||
$value = $personalLikes;
|
||||
}
|
||||
|
||||
$personalBadges[] = [
|
||||
'id' => $badge['id'],
|
||||
'title' => $badge['title'],
|
||||
'description' => $badge['description'],
|
||||
'earned' => $value >= $badge['target'],
|
||||
'progress' => $value,
|
||||
'target' => $badge['target'],
|
||||
];
|
||||
}
|
||||
|
||||
$personal = [
|
||||
'guest_name' => $guestIdentifier,
|
||||
'photos' => $personalPhotos,
|
||||
'tasks' => $personalTasks,
|
||||
'likes' => $personalLikes,
|
||||
'badges' => $personalBadges,
|
||||
];
|
||||
}
|
||||
|
||||
$topUploads = DB::table('photos')
|
||||
->select('guest_name', DB::raw('COUNT(*) as total'), DB::raw('SUM(likes_count) as likes'))
|
||||
->where('event_id', $eventId)
|
||||
->groupBy('guest_name')
|
||||
->orderByDesc('total')
|
||||
->limit(5)
|
||||
->get()
|
||||
->map(fn ($row) => [
|
||||
'guest' => $row->guest_name,
|
||||
'photos' => (int) $row->total,
|
||||
'likes' => (int) $row->likes,
|
||||
])
|
||||
->values();
|
||||
|
||||
$topLikes = DB::table('photos')
|
||||
->select('guest_name', DB::raw('SUM(likes_count) as likes'), DB::raw('COUNT(*) as total'))
|
||||
->where('event_id', $eventId)
|
||||
->groupBy('guest_name')
|
||||
->orderByDesc('likes')
|
||||
->limit(5)
|
||||
->get()
|
||||
->map(fn ($row) => [
|
||||
'guest' => $row->guest_name,
|
||||
'likes' => (int) $row->likes,
|
||||
'photos' => (int) $row->total,
|
||||
])
|
||||
->values();
|
||||
|
||||
$topPhotoRow = DB::table('photos')
|
||||
->leftJoin('tasks', 'photos.task_id', '=', 'tasks.id')
|
||||
->where('photos.event_id', $eventId)
|
||||
->orderByDesc('photos.likes_count')
|
||||
->orderByDesc('photos.created_at')
|
||||
->select([
|
||||
'photos.id',
|
||||
'photos.guest_name',
|
||||
'photos.likes_count',
|
||||
'photos.file_path',
|
||||
'photos.thumbnail_path',
|
||||
'photos.created_at',
|
||||
'tasks.title as task_title',
|
||||
])
|
||||
->first();
|
||||
|
||||
$topPhoto = $topPhotoRow ? [
|
||||
'photo_id' => (int) $topPhotoRow->id,
|
||||
'guest' => $topPhotoRow->guest_name,
|
||||
'likes' => (int) $topPhotoRow->likes_count,
|
||||
'task' => $topPhotoRow->task_title,
|
||||
'created_at' => $topPhotoRow->created_at,
|
||||
'thumbnail' => $this->toPublicUrl($topPhotoRow->thumbnail_path ?: $topPhotoRow->file_path),
|
||||
] : null;
|
||||
|
||||
$trendingEmotionRow = DB::table('photos')
|
||||
->join('emotions', 'photos.emotion_id', '=', 'emotions.id')
|
||||
->where('photos.event_id', $eventId)
|
||||
->select('emotions.id', 'emotions.name', DB::raw('COUNT(*) as total'))
|
||||
->groupBy('emotions.id', 'emotions.name')
|
||||
->orderByDesc('total')
|
||||
->first();
|
||||
|
||||
$trendingEmotion = $trendingEmotionRow ? [
|
||||
'emotion_id' => (int) $trendingEmotionRow->id,
|
||||
'name' => $this->getLocalized($trendingEmotionRow->name, $locale, $trendingEmotionRow->name),
|
||||
'count' => (int) $trendingEmotionRow->total,
|
||||
] : null;
|
||||
|
||||
$timeline = DB::table('photos')
|
||||
->where('event_id', $eventId)
|
||||
->selectRaw('DATE(created_at) as day, COUNT(*) as photos, COUNT(DISTINCT guest_name) as guests')
|
||||
->groupBy('day')
|
||||
->orderBy('day')
|
||||
->limit(14)
|
||||
->get()
|
||||
->map(fn ($row) => [
|
||||
'date' => $row->day,
|
||||
'photos' => (int) $row->photos,
|
||||
'guests' => (int) $row->guests,
|
||||
])
|
||||
->values();
|
||||
|
||||
$feed = DB::table('photos')
|
||||
->leftJoin('tasks', 'photos.task_id', '=', 'tasks.id')
|
||||
->where('photos.event_id', $eventId)
|
||||
->orderByDesc('photos.created_at')
|
||||
->limit(12)
|
||||
->get([
|
||||
'photos.id',
|
||||
'photos.guest_name',
|
||||
'photos.thumbnail_path',
|
||||
'photos.file_path',
|
||||
'photos.likes_count',
|
||||
'photos.created_at',
|
||||
'tasks.title as task_title',
|
||||
])
|
||||
->map(fn ($row) => [
|
||||
'photo_id' => (int) $row->id,
|
||||
'guest' => $row->guest_name,
|
||||
'task' => $row->task_title,
|
||||
'likes' => (int) $row->likes_count,
|
||||
'created_at' => $row->created_at,
|
||||
'thumbnail' => $this->toPublicUrl($row->thumbnail_path ?: $row->file_path),
|
||||
])
|
||||
->values();
|
||||
|
||||
$payload = [
|
||||
'summary' => $summary,
|
||||
'personal' => $personal,
|
||||
'leaderboards' => [
|
||||
'uploads' => $topUploads,
|
||||
'likes' => $topLikes,
|
||||
],
|
||||
'highlights' => [
|
||||
'top_photo' => $topPhoto,
|
||||
'trending_emotion' => $trendingEmotion,
|
||||
'timeline' => $timeline,
|
||||
],
|
||||
'feed' => $feed,
|
||||
];
|
||||
|
||||
$etag = sha1(json_encode($payload));
|
||||
$ifNoneMatch = $request->headers->get('If-None-Match');
|
||||
if ($ifNoneMatch && $ifNoneMatch === $etag) {
|
||||
return response('', 304);
|
||||
}
|
||||
|
||||
return response()->json($payload)
|
||||
->header('Cache-Control', 'no-store')
|
||||
->header('ETag', $etag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user