Allowed status filter values. */ private const ALLOWED_STATUSES = [ 'drafted', 'queued', 'sending', 'sent', 'failed', 'cancelled', ]; /** * Constructor. * * @param NotificationLogRepository $logs Logs. */ public function __construct( private readonly NotificationLogRepository $logs, ) {} /** * Handle a list request. * * @param array $query Query params. * @return array{items: list>, total: int, page: int, perPage: int} * * @since 0.1.0 */ public function handle( array $query ): array { $page = max( 1, (int) ( $query['page'] ?? 1 ) ); $per_page = min( 200, max( 1, (int) ( $query['per_page'] ?? 50 ) ) ); $filters = []; $status = isset( $query['status'] ) && is_string( $query['status'] ) ? strtolower( trim( $query['status'] ) ) : ''; if ( '' !== $status && in_array( $status, self::ALLOWED_STATUSES, true ) ) { $filters['status'] = $status; } $channel = isset( $query['channel'] ) && is_string( $query['channel'] ) ? strtolower( trim( $query['channel'] ) ) : ''; if ( '' !== $channel && preg_match( '/^[a-z0-9_-]+$/', $channel ) ) { $filters['channel'] = $channel; } $booking_id = isset( $query['booking_id'] ) ? (int) $query['booking_id'] : 0; if ( $booking_id > 0 ) { $filters['booking_id'] = $booking_id; } $search = isset( $query['search'] ) && is_string( $query['search'] ) ? trim( $query['search'] ) : ''; if ( '' !== $search ) { $filters['search'] = $search; } $result = $this->logs->list_for_admin( $filters, $page, $per_page ); $items = []; foreach ( $result['items'] as $entry ) { $items[] = $this->to_admin_array( $entry ); } return [ 'items' => $items, 'total' => $result['total'], 'page' => $page, 'perPage' => $per_page, ]; } /** * Map a log entry to the admin API shape. * * @param NotificationLogEntry $entry Entry. * @return array */ private function to_admin_array( NotificationLogEntry $entry ): array { $sent_at = $entry->sent_at() ?? $entry->scheduled_for(); return [ 'id' => (int) $entry->id(), 'uuid' => $entry->uuid()->to_string(), 'booking_id' => $entry->booking_id(), 'customer_id' => $entry->customer_id(), 'template_key' => $entry->template_key(), 'channel' => $entry->channel(), 'recipient' => $entry->recipient(), 'subject' => $entry->subject(), 'body' => $entry->body(), 'status' => $entry->status()->value, 'sent_at' => null === $sent_at ? null : $sent_at->format( 'Y-m-d\TH:i:s\Z' ), 'error' => $entry->last_error(), 'created_at' => $entry->created_at()->format( 'Y-m-d\TH:i:s\Z' ), ]; } }