die tenant admin oauth authentifizierung wurde implementiert und funktioniert jetzt. Zudem wurde das marketing frontend dashboard implementiert.

This commit is contained in:
Codex Agent
2025-11-04 16:14:17 +01:00
parent 92e64c361a
commit fe380689fb
63 changed files with 4239 additions and 1142 deletions

View File

@@ -2,101 +2,189 @@
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
use App\Models\{Event, Task, Emotion, Photo, PhotoLike, Tenant};
use Illuminate\Support\Facades\File;
use App\Models\Emotion;
use App\Models\Event;
use App\Models\Photo;
use App\Models\PhotoLike;
use App\Models\Task;
use App\Models\Tenant;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class DemoPhotosSeeder extends Seeder
{
public function run(): void
{
// Get demo event and tenant
$demoEvent = Event::where('slug', 'demo-wedding-2025')->first();
$demoTenant = Tenant::where('slug', 'demo')->first();
if (!$demoEvent || !$demoTenant) {
$this->command->info('Demo event or tenant not found, skipping DemoPhotosSeeder');
$tenant = Tenant::where('slug', 'demo-tenant')->first();
if (! $tenant) {
$this->command->info('Demo tenant not found, skipping DemoPhotosSeeder');
return;
}
$photoDir = storage_path('app/public/photos');
if (! File::exists($photoDir)) {
$this->command->info('No demo photos available in storage/app/public/photos');
return;
}
$photoFiles = collect(File::files($photoDir))
->filter(fn ($file) => str_ends_with(strtolower($file->getFilename()), '.jpg'))
->values();
if ($photoFiles->isEmpty()) {
$this->command->info('No JPG demo photos found, skipping DemoPhotosSeeder');
return;
}
// Get all available tasks and emotions
$tasks = Task::where('tenant_id', $demoTenant->id)->get();
$emotions = Emotion::all();
if ($emotions->isEmpty()) {
$this->command->info('No emotions available, skipping DemoPhotosSeeder');
if ($tasks->isEmpty() || $emotions->isEmpty()) {
$this->command->info('No tasks or emotions found, skipping DemoPhotosSeeder');
return;
}
// List of 20 guest names (ASCII only to avoid encoding issues)
$guestNames = [
'Anna Mueller', 'Max Schmidt', 'Lisa Weber', 'Tom Fischer', 'Sophie Bauer',
'Lukas Hoffmann', 'Emma Wagner', 'Jonas Klein', 'Mia Schwarz', 'Felix Becker',
'Lena Richter', 'Paul Lehmann', 'Julia Neumann', 'David Vogel', 'Sara Krueger',
'Tim Berger', 'Nina Wolf', 'Ben Schaefer', 'Laura Stein', 'Moritz Fuchs'
$events = [
[
'model' => Event::with(['tasks', 'eventPackage', 'eventPackages', 'eventType'])
->where('slug', 'demo-wedding-2025')
->first(),
'guest_names' => [
'Anna Mueller', 'Max Schmidt', 'Lisa Weber', 'Tom Fischer', 'Sophie Bauer',
'Lukas Hoffmann', 'Emma Wagner', 'Jonas Klein', 'Mia Schwarz', 'Felix Becker',
'Lena Richter', 'Paul Lehmann', 'Julia Neumann', 'David Vogel', 'Sara Krueger',
'Tim Berger', 'Nina Wolf', 'Ben Schaefer', 'Laura Stein', 'Moritz Fuchs',
],
'like_range' => [4, 18],
],
[
'model' => Event::with(['tasks', 'eventPackage', 'eventPackages', 'eventType'])
->where('slug', 'demo-corporate-2025')
->first(),
'guest_names' => [
'Clara Schmidt', 'Jan Becker', 'Noah Winkler', 'Sina Albrecht', 'Kai Lenz',
'Tara Nguyen', 'Omar Hassan', 'Elias Roth', 'Greta Sommer', 'Leonard Busch',
'Verena Graf', 'Nico Adler', 'Johanna Kurz', 'Fabian Scholz', 'Mara Kranz',
'Yuki Tanaka', 'Mateo Ruiz', 'Amina Korb', 'Philipp Krüger', 'Selma Vogt',
],
'like_range' => [2, 12],
],
];
// Get all photo files from storage
$photoDir = storage_path('app/public/photos');
$photoFiles = File::files($photoDir);
$seededCount = 0;
foreach ($photoFiles as $file) {
$filename = $file->getFilename();
if (!str_ends_with($filename, '.jpg')) {
foreach ($events as $config) {
/** @var Event|null $event */
$event = $config['model'];
if (! $event) {
$this->command->warn('Demo event missing, skipping photo seeding.');
continue;
}
// Check if already seeded (avoid duplicates)
if (Photo::where('file_path', 'photos/' . $filename)->exists()) {
$taskIds = $event->tasks()->pluck('tasks.id')->all();
if ($taskIds === []) {
$eventTypeId = $event->event_type_id ?? optional($event->eventType)->id;
$taskIds = Task::where('event_type_id', $eventTypeId)->pluck('id')->all();
}
if ($taskIds === []) {
$this->command->warn(sprintf('No tasks assigned to %s. Photos will be seeded without task references.', $event->slug));
}
$guestNames = $config['guest_names'];
$photosToSeed = min($photoFiles->count(), count($guestNames));
if ($photosToSeed === 0) {
continue;
}
// Generate thumbnail path
$thumbnailFilename = str_replace('.jpg', '_thumb.jpg', $filename);
$thumbnailPath = 'thumbnails/' . $thumbnailFilename;
$storage = Storage::disk('public');
$storage->makeDirectory("events/{$event->id}/gallery");
$storage->makeDirectory("events/{$event->id}/gallery/thumbs");
// Random assignments
$randomTask = $tasks->random();
$randomEmotion = $emotions->random();
$randomGuest = $guestNames[array_rand($guestNames)];
$randomLikes = rand(0, 20);
$eventDate = $demoEvent->date;
$randomUploadedAt = Carbon::parse($eventDate)->addHours(rand(0, 24))->addMinutes(rand(0, 59));
$photosSeeded = 0;
// Create photo
$photo = Photo::create([
'tenant_id' => $demoTenant->id,
'event_id' => $demoEvent->id,
'task_id' => $randomTask->id,
'emotion_id' => $randomEmotion->id,
'guest_name' => $randomGuest,
'file_path' => 'photos/' . $filename,
'thumbnail_path' => $thumbnailPath,
'likes_count' => $randomLikes,
'is_featured' => false,
'metadata' => [],
'created_at' => $randomUploadedAt,
'updated_at' => $randomUploadedAt,
]);
for ($i = 0; $i < $photosToSeed; $i++) {
$sourceFile = $photoFiles->get($i % $photoFiles->count());
$baseName = pathinfo($sourceFile->getFilename(), PATHINFO_FILENAME);
$guestName = $guestNames[$i];
$likes = rand($config['like_range'][0], $config['like_range'][1]);
$timestamp = Carbon::parse($event->date ?? now())
->addHours(rand(0, 36))
->addMinutes(rand(0, 59));
// Add random likes
if ($randomLikes > 0) {
for ($i = 0; $i < $randomLikes; $i++) {
$filename = sprintf('%s-demo-%02d.jpg', $event->slug, $i + 1);
$destPath = "events/{$event->id}/gallery/{$filename}";
if (! $storage->exists($destPath)) {
$storage->put($destPath, File::get($sourceFile->getRealPath()));
}
$thumbFilename = sprintf('%s-demo-%02d_thumb.jpg', $event->slug, $i + 1);
$thumbDest = "events/{$event->id}/gallery/thumbs/{$thumbFilename}";
$existingThumb = "thumbnails/{$baseName}_thumb.jpg";
if ($storage->exists($existingThumb)) {
if (! $storage->exists($thumbDest)) {
$storage->copy($existingThumb, $thumbDest);
}
} else {
if (! $storage->exists($thumbDest)) {
$storage->put($thumbDest, File::get($sourceFile->getRealPath()));
}
}
$taskId = $taskIds ? $taskIds[array_rand($taskIds)] : null;
$emotionId = $emotions->random()->id;
$photo = Photo::updateOrCreate(
[
'tenant_id' => $tenant->id,
'event_id' => $event->id,
'file_path' => $destPath,
],
[
'task_id' => $taskId,
'emotion_id' => $emotionId,
'guest_name' => $guestName,
'thumbnail_path' => $thumbDest,
'likes_count' => $likes,
'is_featured' => $i === 0,
'metadata' => ['demo' => true],
'created_at' => $timestamp,
'updated_at' => $timestamp,
]
);
PhotoLike::where('photo_id', $photo->id)->delete();
$maxLikes = min($likes, 10);
for ($like = 0; $like < $maxLikes; $like++) {
PhotoLike::create([
'photo_id' => $photo->id,
'guest_name' => 'GuestLike_' . Str::random(6),
'ip_address' => '10.0.' . rand(0, 254) . '.' . rand(1, 254),
'created_at' => $randomUploadedAt->clone()->addMinutes(rand(0, 60)),
'guest_name' => 'GuestLike_'.Str::random(6),
'ip_address' => '10.0.'.rand(0, 254).'.'.rand(1, 254),
'created_at' => $timestamp->copy()->addMinutes($like * 3),
]);
}
$photosSeeded++;
}
$seededCount++;
}
$eventPackage = $event->eventPackage ?? $event->eventPackages()->orderByDesc('purchased_at')->first();
if ($eventPackage) {
$eventPackage->forceFill([
'used_photos' => max($eventPackage->used_photos ?? 0, $photosSeeded),
'used_guests' => max($eventPackage->used_guests ?? 0, count(array_unique($guestNames))),
])->save();
}
$this->command->info(sprintf('Seeded %d demo photos with random tasks, emotions, uploaders, and likes', $seededCount));
$this->command->info(sprintf('Seeded %d demo photos for %s', $photosSeeded, $event->slug));
}
}
}