option('plan')); if (! in_array($plan, ['free', 'pro', 'enterprise'], true)) { $this->error('Plan must be free, pro, or enterprise.'); return self::FAILURE; } $user = $this->resolveUser(); if (! $user) { return self::FAILURE; } if ($this->option('reset')) { $this->resetDemoData($user); $this->info('Reset demo data for '.$user->email); } $this->assignPlan($user, $plan, $subscriptions); if ($this->hasDemoMarker($user) && ! $this->option('reset')) { $this->info('Demo data already present; plan updated. Use --reset to reseed.'); $this->printSummary($user, $plan, $subscriptions); return self::SUCCESS; } $this->seedEvents($user, $plan); $this->printSummary($user, $plan, $subscriptions); return self::SUCCESS; } private function resolveUser(): ?User { $identity = trim((string) $this->argument('identity')); $publicIdOpt = trim((string) ($this->option('public-id') ?: '')); $emailOpt = trim((string) ($this->option('email') ?: '')); $nameOpt = trim((string) ($this->option('name') ?: '')); $user = User::query() ->where('email', $identity) ->orWhere('public_id', $identity) ->first(); if ($user) { return $user; } $publicId = $publicIdOpt !== '' ? $publicIdOpt : (Str::isUuid($identity) ? $identity : ''); $email = $emailOpt !== '' ? $emailOpt : (filter_var($identity, FILTER_VALIDATE_EMAIL) ? $identity : ''); if ($publicId === '' || $email === '') { $this->error('Local user not found for "'.$identity.'". Pass --public-id and --email to create a mirror.'); return null; } return User::query()->updateOrCreate( ['public_id' => $publicId], [ 'email' => $email, 'name' => $nameOpt !== '' ? $nameOpt : Str::before($email, '@'), 'email_verified_at' => now(), ], ); } private function assignPlan(User $user, string $plan, SubscriptionService $subscriptions): void { if ($plan === 'free') { ProSubscription::query()->where('user_id', $user->id)->delete(); return; } $planKey = $plan === 'enterprise' ? ProSubscription::PLAN_ENTERPRISE : ProSubscription::PLAN_PRO; ProSubscription::query()->updateOrCreate( ['user_id' => $user->id], [ 'plan' => $planKey, 'status' => ProSubscription::STATUS_ACTIVE, 'price_minor' => $planKey === ProSubscription::PLAN_ENTERPRISE ? $subscriptions->enterprisePriceMinor() : $subscriptions->priceMinor(), 'currency' => (string) config('events.pro.currency', 'GHS'), 'auto_renew' => false, 'started_at' => now(), 'current_period_end' => now()->addYear(), 'last_charged_at' => null, 'canceled_at' => null, 'last_reference' => 'demo-seed-'.$planKey, 'last_error' => null, ], ); } private function hasDemoMarker(User $user): bool { return QrCode::query() ->where('user_id', $user->id) ->where('type', QrCode::TYPE_EVENT) ->where('short_code', 'like', $this->codePrefix($user).'%') ->exists(); } private function codePrefix(User $user): string { return 'dm'.substr(md5((string) $user->public_id), 0, 8); } private function resetDemoData(User $user): void { DB::transaction(function () use ($user) { PaymentGatewaySetting::query()->where('owner_ref', $user->public_id)->delete(); QrTeamMember::query() ->where('account_id', $user->id) ->orWhere('user_id', $user->id) ->delete(); $codes = QrCode::query()->where('user_id', $user->id)->pluck('id'); if ($codes->isNotEmpty()) { QrEventRegistration::query()->whereIn('qr_code_id', $codes)->delete(); QrCode::query()->whereIn('id', $codes)->delete(); } QrEventRegistration::query()->where('user_id', $user->id)->delete(); ProSubscription::query()->where('user_id', $user->id)->delete(); }); } private function seedEvents(User $user, string $plan): void { $prefix = $this->codePrefix($user); $paid = $plan !== 'free'; $eventCount = $paid ? ($plan === 'enterprise' ? 5 : 4) : 2; $regsPerEvent = $paid ? ($plan === 'enterprise' ? 40 : 25) : 35; $keynoteSpeaker = DemoWorld::personByKey('ama-mensah'); $breakoutSpeaker = DemoWorld::personByKey('kojo-darko'); $programmeId = null; if ($paid) { $programme = QrCode::query()->updateOrCreate( ['short_code' => $prefix.'-itn'], [ 'user_id' => $user->id, 'type' => QrCode::TYPE_ITINERARY, 'label' => 'Demo Summit Programme', 'payload' => [ 'content' => [ 'title' => 'Accra Tech Summit Programme', 'subtitle' => 'Two-day agenda', 'description' => 'Demo programme for sales walkthroughs.', 'event_date' => now()->addDays(14)->toDateString(), 'location' => 'Kempinski Accra', 'brand_color' => '#0F766E', 'days' => [[ 'label' => 'Day 1', 'date' => now()->addDays(14)->toDateString(), 'items' => [ [ 'ref' => 'day0-item0', 'time' => '09:00', 'title' => 'Opening keynote', 'description' => 'Welcome and product roadmap', 'location' => 'Main hall', 'host' => DemoWorld::fullName($keynoteSpeaker), 'host_speaker_email' => $keynoteSpeaker['email'], ], [ 'ref' => 'day0-item1', 'time' => '11:00', 'title' => 'Breakout: ticketing', 'description' => 'Paid tiers and badges', 'location' => 'Room B', 'host' => DemoWorld::fullName($breakoutSpeaker), 'host_speaker_email' => $breakoutSpeaker['email'], ], ], ]], ], 'style' => [], ], 'is_active' => true, ], ); $programmeId = $programme->id; } for ($i = 1; $i <= $eventCount; $i++) { $mode = $paid ? 'ticketing' : 'free'; $tiers = $paid ? [ ['name' => 'General', 'price' => 150, 'capacity' => 200], ['name' => 'VIP', 'price' => 450, 'capacity' => 40], ] : [['name' => 'Registration', 'price' => 0, 'capacity' => 100]]; $event = QrCode::query()->updateOrCreate( ['short_code' => $prefix.'-e'.$i], [ 'user_id' => $user->id, 'type' => QrCode::TYPE_EVENT, 'label' => $paid ? 'Demo Paid Event '.$i : 'Demo Free Event '.$i, 'payload' => [ 'content' => [ 'name' => $paid ? 'Accra Business Forum '.$i : 'Community Meetup '.$i, 'tagline' => $paid ? 'Ticketed demo event' : 'Free RSVP demo event', 'description' => 'Seeded by demo:seed for sales walkthroughs.', 'location' => 'Accra, Ghana', 'starts_at' => now()->addDays(7 + $i)->toIso8601String(), 'ends_at' => now()->addDays(7 + $i)->addHours(6)->toIso8601String(), 'organizer' => DemoWorld::business($plan)['name'], 'currency' => 'GHS', 'mode' => $mode, 'format' => $i === 1 ? 'in_person' : 'hybrid', 'tiers' => $tiers, 'badge_fields' => ['company', 'role'], 'badge_size' => 'standard', 'accepts_payment' => $paid, 'registration_open' => true, 'programme_qr_id' => $programmeId, 'speakers' => $paid ? [ [ 'name' => DemoWorld::fullName($keynoteSpeaker), 'email' => $keynoteSpeaker['email'], 'role' => 'Keynote', ], ] : [], ], 'style' => [], ], 'is_active' => true, ], ); $this->seedRegistrations($user, $event, $regsPerEvent, $paid, $prefix, $i); } if ($paid) { PaymentGatewaySetting::query()->updateOrCreate( ['owner_ref' => $user->public_id], [ 'provider' => PaymentGatewaySetting::PROVIDER_PAYSTACK, 'public_key' => 'pk_test_demo_events', 'secret_key' => 'sk_test_demo_events', 'is_active' => true, 'metadata' => ['demo' => true], ], ); } } private function seedRegistrations( User $user, QrCode $event, int $count, bool $paid, string $prefix, int $eventIndex, ): void { $existing = QrEventRegistration::query() ->where('qr_code_id', $event->id) ->where('reference', 'like', $prefix.'%') ->count(); if ($existing >= $count) { return; } $people = DemoWorld::people(); for ($n = $existing + 1; $n <= $count; $n++) { $tier = $paid ? ($n % 5 === 0 ? 'VIP' : 'General') : 'Registration'; $amount = $paid ? ($tier === 'VIP' ? 45000 : 15000) : 0; $ref = sprintf('%s-r%02d-%03d', $prefix, $eventIndex, $n); $badge = strtoupper(substr($prefix, 2, 4).sprintf('%02d%03d', $eventIndex, $n)); $person = $people[($n - 1) % count($people)]; QrEventRegistration::query()->updateOrCreate( ['reference' => $ref], [ 'qr_code_id' => $event->id, 'user_id' => $user->id, 'badge_code' => substr($badge, 0, 16), 'tier_name' => $tier, 'amount_minor' => $amount, 'currency' => 'GHS', 'attendee_name' => DemoWorld::fullName($person), 'attendee_email' => $person['email'], 'attendee_phone' => $person['phone'], 'badge_fields' => [ 'company' => DemoWorld::companyNameForPerson($person), 'role' => $tier === 'VIP' ? 'Executive' : 'Guest', ], 'status' => QrEventRegistration::STATUS_CONFIRMED, 'payment_reference' => $paid ? 'demo-pay-'.$ref : null, 'paid_at' => $paid ? now()->subDays(max(1, 30 - $n)) : null, 'checked_in_at' => $n % 4 === 0 ? now()->subHours($n) : null, 'metadata' => ['demo' => true], ], ); } } private function printSummary(User $user, string $plan, SubscriptionService $subscriptions): void { $events = QrCode::query() ->where('user_id', $user->id) ->where('type', QrCode::TYPE_EVENT) ->count(); $programmes = QrCode::query() ->where('user_id', $user->id) ->where('type', QrCode::TYPE_ITINERARY) ->count(); $regs = QrEventRegistration::query()->where('user_id', $user->id)->count(); $this->table( ['Field', 'Value'], [ ['email', $user->email], ['public_id', $user->public_id], ['requested_plan', $plan], ['effective_plan', $subscriptions->planKey($user)], ['live_events', (string) $events], ['programmes', (string) $programmes], ['registrations', (string) $regs], ], ); } }