option('all') ? $this->reviewAny() : $this->reviewPending(); } private function reviewPending(): int { $pending = $this->load(IntegrationRequest::query()->where('status', IntegrationRequestStatus::Pending)); if ($pending->isEmpty()) { $this->info('No pending integration requests.'); return self::SUCCESS; } $this->printTable($pending); $changed = 0; foreach ($pending as $request) { $decision = $this->choice( "Review \"{$request->name}\" ({$request->url})", ['approve', 'in progress', 'reject', 'not doable', 'done', 'skip'], 'skip', ); if ($decision !== 'skip' && $this->apply($request, $decision)) { $changed++; } } $this->info("Done. Updated {$changed} of {$pending->count()} requests."); return self::SUCCESS; } private function reviewAny(): int { $requests = $this->load(IntegrationRequest::query()); if ($requests->isEmpty()) { $this->info('No integration requests.'); return self::SUCCESS; } $this->printTable($requests, withIndex: true); $request = $requests->get((int) $this->ask('Which request do you want to update? (number)') - 1); if ($request === null) { $this->error('That number is not on the list.'); return self::FAILURE; } $this->apply($request, $this->choice( "New status for \"{$request->name}\"", ['approve', 'in progress', 'reject', 'not doable', 'done'], )); $this->info("\"{$request->name}\" is now {$request->status->label()}."); return self::SUCCESS; } private function apply(IntegrationRequest $request, string $decision): bool { $status = match ($decision) { 'approve' => IntegrationRequestStatus::Approved, 'in progress' => IntegrationRequestStatus::InProgress, 'reject' => IntegrationRequestStatus::Rejected, 'not doable' => IntegrationRequestStatus::NotDoable, 'done' => IntegrationRequestStatus::Done, default => null, }; if ($status === null) { return false; } $request->update([ 'status' => $status, 'comment' => $this->commentFor($status), ]); return true; } private function commentFor(IntegrationRequestStatus $status): ?string { if ($status === IntegrationRequestStatus::NotDoable) { $comment = $this->ask('Why is this integration not doable? (shown to users)'); while (blank($comment)) { $comment = $this->ask('A comment is required to mark an integration as not doable'); } return $comment; } if ($status === IntegrationRequestStatus::InProgress) { return $this->ask('Add a comment for this request (optional, shown to users)') ?: null; } // Approving, rejecting, re-queuing or marking done drops any stale public comment. return null; } /** * @param Builder $query * @return Collection */ private function load(Builder $query): Collection { return $query ->withCount('votes') // Authors who deleted their account are soft-deleted, but their requests // linger; load them trashed so the table still shows who submitted each one. ->with(['user' => fn ($user) => $user->withTrashed()->select('id', 'email')]) ->orderBy('created_at') ->get(); } /** * @param Collection $requests */ private function printTable(Collection $requests, bool $withIndex = false): void { $headers = ['Name', 'URL', 'Status', 'Submitted by', 'Votes', 'Created']; if ($withIndex) { array_unshift($headers, '#'); } $rows = $requests->values()->map(function (IntegrationRequest $request, int $index) use ($withIndex): array { $row = [ $request->name, $request->url, $request->status->label(), $request->user->email, $request->votes_count, $request->created_at?->format('Y-m-d') ?? '—', ]; if ($withIndex) { array_unshift($row, $index + 1); } return $row; })->all(); $this->table($headers, $rows); } }