> */ public array $gapRows = []; public string $context = 'default'; /** * @param list> $buckets */ public function mount(array $buckets = [], string $context = 'default'): void { $this->gapRows = BaselineCompareEvidenceGapDetails::tableRows($buckets); $this->context = $context; } public function table(Table $table): Table { return $table ->queryStringIdentifier('baselineCompareEvidenceGap'.Str::studly($this->context)) ->defaultSort('reason_label') ->defaultPaginationPageOption(10) ->paginated(TablePaginationProfiles::picker()) ->searchable() ->searchPlaceholder(__('baseline-compare.evidence_gap_search_placeholder')) ->records(function ( ?string $sortColumn, ?string $sortDirection, ?string $search, array $filters, int $page, int $recordsPerPage ): LengthAwarePaginator { $rows = $this->filterRows( rows: collect($this->gapRows), search: $search, filters: $filters, ); $rows = $this->sortRows( rows: $rows, sortColumn: $sortColumn, sortDirection: $sortDirection, ); return $this->paginateRows( rows: $rows, page: $page, recordsPerPage: $recordsPerPage, ); }) ->filters([ SelectFilter::make('reason_code') ->label(__('baseline-compare.evidence_gap_reason')) ->options(BaselineCompareEvidenceGapDetails::reasonFilterOptions($this->gapRows)), SelectFilter::make('policy_type') ->label(__('baseline-compare.evidence_gap_policy_type')) ->options(BaselineCompareEvidenceGapDetails::policyTypeFilterOptions($this->gapRows)), SelectFilter::make('subject_class') ->label(__('baseline-compare.evidence_gap_subject_class')) ->options(BaselineCompareEvidenceGapDetails::subjectClassFilterOptions($this->gapRows)), SelectFilter::make('operator_action_category') ->label(__('baseline-compare.evidence_gap_next_action')) ->options(BaselineCompareEvidenceGapDetails::actionCategoryFilterOptions($this->gapRows)), ]) ->striped() ->deferLoading(! app()->runningUnitTests()) ->columns([ TextColumn::make('reason_label') ->label(__('baseline-compare.evidence_gap_reason')) ->searchable() ->sortable() ->wrap(), TextColumn::make('policy_type') ->label(__('baseline-compare.evidence_gap_policy_type')) ->badge() ->formatStateUsing(TagBadgeRenderer::label(TagBadgeDomain::PolicyType)) ->color(TagBadgeRenderer::color(TagBadgeDomain::PolicyType)) ->icon(TagBadgeRenderer::icon(TagBadgeDomain::PolicyType)) ->iconColor(TagBadgeRenderer::iconColor(TagBadgeDomain::PolicyType)) ->searchable() ->sortable() ->wrap(), TextColumn::make('subject_class_label') ->label(__('baseline-compare.evidence_gap_subject_class')) ->badge() ->searchable() ->sortable() ->wrap(), TextColumn::make('resolution_outcome_label') ->label(__('baseline-compare.evidence_gap_outcome')) ->searchable() ->sortable() ->wrap(), TextColumn::make('operator_action_category_label') ->label(__('baseline-compare.evidence_gap_next_action')) ->searchable() ->sortable() ->wrap(), TextColumn::make('subject_key') ->label(__('baseline-compare.evidence_gap_subject_key')) ->searchable() ->sortable() ->wrap() ->extraAttributes(['class' => 'font-mono text-xs']), ]) ->actions([]) ->bulkActions([]) ->emptyStateHeading(__('baseline-compare.evidence_gap_table_empty_heading')) ->emptyStateDescription(__('baseline-compare.evidence_gap_table_empty_description')); } public function render(): View { return view('livewire.baseline-compare-evidence-gap-table'); } /** * @param Collection> $rows * @param array $filters * @return Collection> */ private function filterRows(Collection $rows, ?string $search, array $filters): Collection { $normalizedSearch = Str::lower(trim((string) $search)); $reasonCode = $filters['reason_code']['value'] ?? null; $policyType = $filters['policy_type']['value'] ?? null; $subjectClass = $filters['subject_class']['value'] ?? null; $operatorActionCategory = $filters['operator_action_category']['value'] ?? null; return $rows ->when( $normalizedSearch !== '', function (Collection $rows) use ($normalizedSearch): Collection { return $rows->filter(function (array $row) use ($normalizedSearch): bool { return str_contains(Str::lower((string) ($row['search_text'] ?? '')), $normalizedSearch); }); } ) ->when( filled($reasonCode), fn (Collection $rows): Collection => $rows->where('reason_code', (string) $reasonCode) ) ->when( filled($policyType), fn (Collection $rows): Collection => $rows->where('policy_type', (string) $policyType) ) ->when( filled($subjectClass), fn (Collection $rows): Collection => $rows->where('subject_class', (string) $subjectClass) ) ->when( filled($operatorActionCategory), fn (Collection $rows): Collection => $rows->where('operator_action_category', (string) $operatorActionCategory) ) ->values(); } /** * @param Collection> $rows * @return Collection> */ private function sortRows(Collection $rows, ?string $sortColumn, ?string $sortDirection): Collection { if (! filled($sortColumn)) { return $rows; } $direction = Str::lower((string) ($sortDirection ?? 'asc')) === 'desc' ? 'desc' : 'asc'; return $rows->sortBy( fn (array $row): string => (string) ($row[$sortColumn] ?? ''), SORT_NATURAL | SORT_FLAG_CASE, $direction === 'desc' )->values(); } /** * @param Collection> $rows */ private function paginateRows(Collection $rows, int $page, int $recordsPerPage): LengthAwarePaginator { $perPage = max(1, $recordsPerPage); $currentPage = max(1, $page); $total = $rows->count(); $items = $rows->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $items, $total, $perPage, $currentPage, ); } }