From 2d92b8f4957740e5df5fcbe1982ad9226aaf5bc3 Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 21 Jan 2025 13:58:16 +0000 Subject: [PATCH 1/5] Allow component groups to be advanced ordered --- ...rdering_cols_to_component_groups_table.php | 35 ++++++++++++++ resources/lang/en/component_group.php | 3 ++ resources/lang/en/resource.php | 11 +++++ .../components/component-groups.blade.php | 7 +++ resources/views/status-page/index.blade.php | 8 +--- src/Enums/ResourceOrderColumnEnum.php | 38 +++++++++++++++ src/Enums/ResourceOrderDirectionEnum.php | 29 +++++++++++ .../Resources/ComponentGroupResource.php | 22 +++++++++ .../StatusPage/StatusPageController.php | 15 ++---- src/Models/Component.php | 15 ++++++ src/Models/ComponentGroup.php | 9 +++- src/Models/Incident.php | 2 +- src/View/Components/ComponentGroups.php | 48 +++++++++++++++++++ 13 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 database/migrations/2025_01_21_121458_add_ordering_cols_to_component_groups_table.php create mode 100644 resources/views/components/component-groups.blade.php create mode 100644 src/Enums/ResourceOrderColumnEnum.php create mode 100644 src/Enums/ResourceOrderDirectionEnum.php create mode 100644 src/View/Components/ComponentGroups.php diff --git a/database/migrations/2025_01_21_121458_add_ordering_cols_to_component_groups_table.php b/database/migrations/2025_01_21_121458_add_ordering_cols_to_component_groups_table.php new file mode 100644 index 00000000..9daf70fc --- /dev/null +++ b/database/migrations/2025_01_21_121458_add_ordering_cols_to_component_groups_table.php @@ -0,0 +1,35 @@ +string('order_column')->nullable()->after('order'); + $table->char('order_direction', 4)->nullable()->after('order_column'); + }); + + DB::table('component_groups')->update(['order_column' => ResourceOrderColumnEnum::Manual->value]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('component_groups', function (Blueprint $table) { + $table->dropColumn([ + 'order_column', + 'order_direction', + ]); + }); + } +}; diff --git a/resources/lang/en/component_group.php b/resources/lang/en/component_group.php index 6eb88f3f..286604b7 100644 --- a/resources/lang/en/component_group.php +++ b/resources/lang/en/component_group.php @@ -13,6 +13,7 @@ 'name' => 'Name', 'visible' => 'Visible', 'collapsed' => 'Collapsed', + 'order_column' => 'Component Group Order', 'created_at' => 'Created at', 'updated_at' => 'Updated at', ], @@ -25,5 +26,7 @@ 'name_label' => 'Name', 'visible_label' => 'Visible', 'collapsed_label' => 'Collapsed', + 'order_column_label' => 'Component Group Order', + 'order_direction' => 'Order Direction', ], ]; diff --git a/resources/lang/en/resource.php b/resources/lang/en/resource.php index fdede18c..8ad1b6e3 100644 --- a/resources/lang/en/resource.php +++ b/resources/lang/en/resource.php @@ -1,6 +1,17 @@ [ + 'id' => 'ID', + 'last_updated' => 'Last Updated', + 'name' => 'Name', + 'manual' => 'Manual', + 'status' => 'Status', + ], + 'order_direction' => [ + 'asc' => 'Ascending', + 'desc' => 'Descending', + ], 'visibility' => [ 'authenticated' => 'Users', 'guest' => 'Guests', diff --git a/resources/views/components/component-groups.blade.php b/resources/views/components/component-groups.blade.php new file mode 100644 index 00000000..8fe49c62 --- /dev/null +++ b/resources/views/components/component-groups.blade.php @@ -0,0 +1,7 @@ +@foreach($componentGroups as $componentGroup) + +@endforeach + +@foreach($ungroupedComponents as $component) + +@endforeach diff --git a/resources/views/status-page/index.blade.php b/resources/views/status-page/index.blade.php index 5fef4e98..adbf7b3a 100644 --- a/resources/views/status-page/index.blade.php +++ b/resources/views/status-page/index.blade.php @@ -6,13 +6,7 @@ - @foreach($componentGroups as $componentGroup) - - @endforeach - - @foreach($ungroupedComponents as $component) - - @endforeach + @if($schedules->isNotEmpty()) diff --git a/src/Enums/ResourceOrderColumnEnum.php b/src/Enums/ResourceOrderColumnEnum.php new file mode 100644 index 00000000..ad56af76 --- /dev/null +++ b/src/Enums/ResourceOrderColumnEnum.php @@ -0,0 +1,38 @@ + __('cachet::resource.order_column.id'), + self::LastUpdated => __('cachet::resource.order_column.last_updated'), + self::Name => __('cachet::resource.order_column.name'), + self::Manual => __('cachet::resource.order_column.manual'), + self::Status => __('cachet::resource.order_column.status'), + }; + } + + /** + * Determine if the column requires a direction. + */ + public static function requiresDirection(): array + { + return [ + self::Id, + self::LastUpdated, + self::Name, + self::Status, + ]; + } +} diff --git a/src/Enums/ResourceOrderDirectionEnum.php b/src/Enums/ResourceOrderDirectionEnum.php new file mode 100644 index 00000000..52e8f723 --- /dev/null +++ b/src/Enums/ResourceOrderDirectionEnum.php @@ -0,0 +1,29 @@ + __('cachet::resource.order_direction.asc'), + self::Desc => __('cachet::resource.order_direction.desc'), + }; + } + + public function ascending(): bool + { + return $this === self::Asc; + } + + public function descending(): bool + { + return $this === self::Desc; + } +} diff --git a/src/Filament/Resources/ComponentGroupResource.php b/src/Filament/Resources/ComponentGroupResource.php index fe3c99a9..0271bd1a 100644 --- a/src/Filament/Resources/ComponentGroupResource.php +++ b/src/Filament/Resources/ComponentGroupResource.php @@ -3,6 +3,8 @@ namespace Cachet\Filament\Resources; use Cachet\Enums\ComponentGroupVisibilityEnum; +use Cachet\Enums\ResourceOrderColumnEnum; +use Cachet\Enums\ResourceOrderDirectionEnum; use Cachet\Enums\ResourceVisibilityEnum; use Cachet\Filament\Resources\ComponentGroupResource\Pages; use Cachet\Filament\Resources\ComponentResource\RelationManagers\ComponentsRelationManager; @@ -45,6 +47,18 @@ public static function form(Form $form): Form ->default(ComponentGroupVisibilityEnum::expanded) ->columnSpanFull(), ]), + Forms\Components\Section::make()->schema([ + Forms\Components\Select::make('order_column') + ->label(__('cachet::component_group.form.order_column_label')) + ->options(ResourceOrderColumnEnum::class) + ->required() + ->reactive(), + Forms\Components\Select::make('order_direction') + ->label(__('cachet::component_group.form.order_direction')) + ->options(ResourceOrderDirectionEnum::class) + ->required(fn (Forms\Get $get) => $get('order_column') !== ResourceOrderColumnEnum::Manual->value) + ->visible(fn (Forms\Get $get) => $get('order_column') !== ResourceOrderColumnEnum::Manual->value), + ]) ]); } @@ -63,6 +77,14 @@ public static function table(Table $table): Table Tables\Columns\TextColumn::make('collapsed') ->label(__('cachet::component_group.list.headers.collapsed')) ->sortable(), + Tables\Columns\TextColumn::make('order_column') + ->icon(fn ($record) => match (true) { + $record->order_column->value === ResourceOrderColumnEnum::Manual->value => 'heroicon-o-chevron-up-down', + $record->order_direction === ResourceOrderDirectionEnum::Asc => 'heroicon-o-arrow-up', + $record->order_direction === ResourceOrderDirectionEnum::Desc => 'heroicon-o-arrow-down', + }) + ->label(__('cachet::component_group.list.headers.order_column')) + ->sortable(), Tables\Columns\TextColumn::make('created_at') ->label(__('cachet::component_group.list.headers.created_at')) ->dateTime() diff --git a/src/Http/Controllers/StatusPage/StatusPageController.php b/src/Http/Controllers/StatusPage/StatusPageController.php index 44179b9a..635d5144 100644 --- a/src/Http/Controllers/StatusPage/StatusPageController.php +++ b/src/Http/Controllers/StatusPage/StatusPageController.php @@ -2,11 +2,14 @@ namespace Cachet\Http\Controllers\StatusPage; +use Cachet\Enums\ResourceOrderColumnEnum; use Cachet\Models\Component; use Cachet\Models\ComponentGroup; use Cachet\Models\Incident; use Cachet\Models\Schedule; +use Filament\Resources\Resource; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Facades\DB; use Illuminate\View\View; class StatusPageController @@ -17,18 +20,6 @@ class StatusPageController public function index(): View { return view('cachet::status-page.index', [ - 'componentGroups' => ComponentGroup::query() - ->with(['components' => fn ($query) => $query->enabled()->orderBy('order')->withCount('incidents')]) - ->visible(auth()->check()) - ->when(auth()->check(), fn (Builder $query) => $query->users(), fn ($query) => $query->guests()) - ->get(), - 'ungroupedComponents' => Component::query() - ->enabled() - ->whereNull('component_group_id') - ->orderBy('order') - ->withCount('incidents') - ->get(), - 'schedules' => Schedule::query()->with('updates')->incomplete()->orderBy('scheduled_at')->get(), ]); } diff --git a/src/Models/Component.php b/src/Models/Component.php index 657ea2c7..b316ba7c 100644 --- a/src/Models/Component.php +++ b/src/Models/Component.php @@ -4,6 +4,7 @@ use Cachet\Database\Factories\ComponentFactory; use Cachet\Enums\ComponentStatusEnum; +use Cachet\Enums\ResourceOrderColumnEnum; use Cachet\Events\Components\ComponentCreated; use Cachet\Events\Components\ComponentDeleted; use Cachet\Events\Components\ComponentUpdated; @@ -138,6 +139,20 @@ public function latestStatus(): Attribute return Attribute::get(fn () => $this->incidents()->latest()->first()?->pivot->component_status ?? $this->status); } + /** + * Determine how to order the component. + */ + public function orderableBy(ComponentGroup $group): mixed + { + return match ($group->order_column) { + ResourceOrderColumnEnum::Id => $this->id, + ResourceOrderColumnEnum::LastUpdated => $this->updated_at, + ResourceOrderColumnEnum::Name => $this->name, + ResourceOrderColumnEnum::Manual => $this->order, + ResourceOrderColumnEnum::Status => $this->status->value, + }; + } + /** * Create a new factory instance for the model. */ diff --git a/src/Models/ComponentGroup.php b/src/Models/ComponentGroup.php index e078911f..08ca405c 100644 --- a/src/Models/ComponentGroup.php +++ b/src/Models/ComponentGroup.php @@ -5,6 +5,8 @@ use Cachet\Concerns\HasVisibility; use Cachet\Database\Factories\ComponentGroupFactory; use Cachet\Enums\ComponentGroupVisibilityEnum; +use Cachet\Enums\ResourceOrderColumnEnum; +use Cachet\Enums\ResourceOrderDirectionEnum; use Cachet\Enums\ResourceVisibilityEnum; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; @@ -34,7 +36,8 @@ class ComponentGroup extends Model /** @var array */ protected $casts = [ - 'order' => 'int', + 'order_column' => ResourceOrderColumnEnum::class, + 'order_direction' => ResourceOrderDirectionEnum::class, 'collapsed' => ComponentGroupVisibilityEnum::class, 'visible' => ResourceVisibilityEnum::class, ]; @@ -43,6 +46,8 @@ class ComponentGroup extends Model protected $fillable = [ 'name', 'order', + 'order_column', + 'order_direction', 'collapsed', 'visible', ]; @@ -54,7 +59,7 @@ class ComponentGroup extends Model */ public function components(): HasMany { - return $this->hasMany(Component::class); + return $this->hasMany(Component::class)->chaperone('group'); } public function isCollapsible(): bool diff --git a/src/Models/Incident.php b/src/Models/Incident.php index 30e334b9..1f7bc510 100644 --- a/src/Models/Incident.php +++ b/src/Models/Incident.php @@ -124,7 +124,7 @@ public function components(): BelongsToMany */ public function incidentComponents(): HasMany { - return $this->hasMany(IncidentComponent::class); + return $this->hasMany(IncidentComponent::class)->chaperone(); } /** diff --git a/src/View/Components/ComponentGroups.php b/src/View/Components/ComponentGroups.php new file mode 100644 index 00000000..0775a624 --- /dev/null +++ b/src/View/Components/ComponentGroups.php @@ -0,0 +1,48 @@ + $this->componentGroups(), + 'ungroupedComponents' => \Cachet\Models\Component::query() + ->enabled() + ->whereNull('component_group_id') + ->orderBy('order') + ->withCount('incidents') + ->get(), + ]); + } + + /** + * Fetch component groups with their components in the configured order. + */ + private function componentGroups(): Collection + { + return ComponentGroup::query() + ->with(['components' => fn ($query) => $query->enabled()->orderBy('order')->withCount('incidents')]) + ->visible(auth()->check()) + ->when(auth()->check(), fn (Builder $query) => $query->users(), fn ($query) => $query->guests()) + ->get() + ->map(function (ComponentGroup $group) { + $group->components = $group->components + ->sortBy( + fn (\Cachet\Models\Component $component) => $component->orderableBy($group), + descending: $group->order_direction->descending() + ); + + return $group; + }); + } +} From b3d05bce66e983fbb495773143569c5a084c0abf Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 21 Jan 2025 21:06:08 +0000 Subject: [PATCH 2/5] wip --- src/View/Components/ComponentGroups.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Components/ComponentGroups.php b/src/View/Components/ComponentGroups.php index 0775a624..2210cad8 100644 --- a/src/View/Components/ComponentGroups.php +++ b/src/View/Components/ComponentGroups.php @@ -39,7 +39,7 @@ private function componentGroups(): Collection $group->components = $group->components ->sortBy( fn (\Cachet\Models\Component $component) => $component->orderableBy($group), - descending: $group->order_direction->descending() + descending: $group->order_direction?->descending() ); return $group; From d09b8b20d6739d30d5f71dd19ca2af7a09b0b9eb Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 21 Jan 2025 21:13:36 +0000 Subject: [PATCH 3/5] wip --- src/View/Components/ComponentGroups.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/View/Components/ComponentGroups.php b/src/View/Components/ComponentGroups.php index 2210cad8..eb9eca94 100644 --- a/src/View/Components/ComponentGroups.php +++ b/src/View/Components/ComponentGroups.php @@ -2,7 +2,6 @@ namespace Cachet\View\Components; -use Cachet\Enums\ResourceOrderColumnEnum; use Cachet\Models\ComponentGroup; use Closure; use Illuminate\Database\Eloquent\Builder; From 580a13b1e8893a2b0a660fcb525e6f0069623aed Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 21 Jan 2025 21:15:21 +0000 Subject: [PATCH 4/5] wip --- src/Filament/Resources/ComponentGroupResource.php | 4 ++-- src/Models/Component.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Filament/Resources/ComponentGroupResource.php b/src/Filament/Resources/ComponentGroupResource.php index 0271bd1a..800bff1a 100644 --- a/src/Filament/Resources/ComponentGroupResource.php +++ b/src/Filament/Resources/ComponentGroupResource.php @@ -44,7 +44,7 @@ public static function form(Form $form): Form ->required() ->inline() ->options(ComponentGroupVisibilityEnum::class) - ->default(ComponentGroupVisibilityEnum::expanded) + ->default(ComponentGroupVisibilityEnum::expanded->value) ->columnSpanFull(), ]), Forms\Components\Section::make()->schema([ @@ -79,7 +79,7 @@ public static function table(Table $table): Table ->sortable(), Tables\Columns\TextColumn::make('order_column') ->icon(fn ($record) => match (true) { - $record->order_column->value === ResourceOrderColumnEnum::Manual->value => 'heroicon-o-chevron-up-down', + $record->order_column === ResourceOrderColumnEnum::Manual => 'heroicon-o-chevron-up-down', $record->order_direction === ResourceOrderDirectionEnum::Asc => 'heroicon-o-arrow-up', $record->order_direction === ResourceOrderDirectionEnum::Desc => 'heroicon-o-arrow-down', }) diff --git a/src/Models/Component.php b/src/Models/Component.php index b316ba7c..8bee2e18 100644 --- a/src/Models/Component.php +++ b/src/Models/Component.php @@ -149,7 +149,7 @@ public function orderableBy(ComponentGroup $group): mixed ResourceOrderColumnEnum::LastUpdated => $this->updated_at, ResourceOrderColumnEnum::Name => $this->name, ResourceOrderColumnEnum::Manual => $this->order, - ResourceOrderColumnEnum::Status => $this->status->value, + default => $this->status->value, }; } From 14da1fd37b7c4801baad70c10c25cc184b272c43 Mon Sep 17 00:00:00 2001 From: James Brooks Date: Tue, 21 Jan 2025 21:20:18 +0000 Subject: [PATCH 5/5] wip --- src/Filament/Resources/ComponentGroupResource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Filament/Resources/ComponentGroupResource.php b/src/Filament/Resources/ComponentGroupResource.php index 800bff1a..8f3bfb53 100644 --- a/src/Filament/Resources/ComponentGroupResource.php +++ b/src/Filament/Resources/ComponentGroupResource.php @@ -82,6 +82,7 @@ public static function table(Table $table): Table $record->order_column === ResourceOrderColumnEnum::Manual => 'heroicon-o-chevron-up-down', $record->order_direction === ResourceOrderDirectionEnum::Asc => 'heroicon-o-arrow-up', $record->order_direction === ResourceOrderDirectionEnum::Desc => 'heroicon-o-arrow-down', + default => null, }) ->label(__('cachet::component_group.list.headers.order_column')) ->sortable(),