diff --git a/.ai/mcp/mcp.json b/.ai/mcp/mcp.json index e69de29b..3e55e326 100644 --- a/.ai/mcp/mcp.json +++ b/.ai/mcp/mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "laravel-boost": { + "command": "/usr/bin/php", + "args": [ + "/home/danielhe4rt/dev/he4rt/he4rtdevs.com/artisan", + "boost:mcp" + ] + } + } +} \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit old mode 100644 new mode 100755 diff --git a/app-modules/activity/src/Filament/Admin/Resources/Messages/MessageResource.php b/app-modules/activity/src/Filament/Admin/Resources/Messages/MessageResource.php deleted file mode 100644 index 1c95155e..00000000 --- a/app-modules/activity/src/Filament/Admin/Resources/Messages/MessageResource.php +++ /dev/null @@ -1,48 +0,0 @@ - ListMessages::route('/'), - 'create' => CreateMessage::route('/create'), - 'edit' => EditMessage::route('/{record}/edit'), - ]; - } -} diff --git a/app-modules/activity/src/Filament/Admin/Resources/Messages/Pages/CreateMessage.php b/app-modules/activity/src/Filament/Admin/Resources/Messages/Pages/CreateMessage.php deleted file mode 100644 index 546c2393..00000000 --- a/app-modules/activity/src/Filament/Admin/Resources/Messages/Pages/CreateMessage.php +++ /dev/null @@ -1,13 +0,0 @@ -components([ - Select::make('external_identity_id') - ->label('Provider') - ->getOptionLabelFromRecordUsing(fn (ExternalIdentity $record) => $record->provider->getLabel()) - ->preload() - ->searchable() - ->relationship( - name: 'provider', - titleAttribute: 'provider', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - - TextInput::make('channel_id') - ->label('Chanel') - ->nullable(), - - Select::make('tenant_id') - ->label('Tenant') - ->preload() - ->searchable() - ->relationship( - name: 'tenant', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->nullable(), - - TextInput::make('provider_message_id') - ->label('Message ID at Provider') - ->maxLength(100) - ->nullable(), - - Textarea::make('content') - ->label('Content') - ->rows(5) - ->required(), - - DateTimePicker::make('sent_at') - ->label('Sent at') - ->nullable(), - - TextInput::make('obtained_experience') - ->label('Experience Gained') - ->numeric() - ->nullable(), - ]); - } -} diff --git a/app-modules/activity/src/Filament/Admin/Resources/Messages/Tables/MessagesTable.php b/app-modules/activity/src/Filament/Admin/Resources/Messages/Tables/MessagesTable.php deleted file mode 100644 index 42e9d8ec..00000000 --- a/app-modules/activity/src/Filament/Admin/Resources/Messages/Tables/MessagesTable.php +++ /dev/null @@ -1,58 +0,0 @@ -columns([ - TextColumn::make('provider.provider') - ->badge() - ->label('Provider') - ->sortable() - ->searchable(), - - TextColumn::make('channel_id') - ->label('Channel') - ->sortable() - ->searchable(), - - TextColumn::make('content') - ->label('Content') - ->limit(60) - ->wrap(), - - TextColumn::make('sent_at') - ->label('Sent') - ->sortable(), - - TextColumn::make('obtained_experience') - ->label('Obteined XP') - ->numeric() - ->sortable(), - - TextColumn::make('created_at') - ->label('Created') - ->dateTime('d/m/Y H:i') - ->sortable(), - ]) - ->recordActions([ - EditAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/activity/src/Providers/ActivityServiceProvider.php b/app-modules/activity/src/Providers/ActivityServiceProvider.php index 435f5772..4616aee0 100644 --- a/app-modules/activity/src/Providers/ActivityServiceProvider.php +++ b/app-modules/activity/src/Providers/ActivityServiceProvider.php @@ -4,24 +4,11 @@ namespace He4rt\Activity\Providers; -use App\Enums\FilamentPanel; -use Filament\Panel; -use He4rt\Activity\Filament\Admin\Resources\Messages\MessageResource; use Illuminate\Support\ServiceProvider; class ActivityServiceProvider extends ServiceProvider { - public function register(): void - { - Panel::configureUsing(function (Panel $panel): void { - match ($panel->currentPanel()) { - FilamentPanel::Admin => $panel->resources([ - MessageResource::class, - ]), - default => null, - }; - }); - } + public function register(): void {} public function boot(): void { diff --git a/app-modules/activity/tests/Feature/Filament/Admin/Message/CreateMessageTest.php b/app-modules/activity/tests/Feature/Filament/Admin/Message/CreateMessageTest.php deleted file mode 100644 index e57ea082..00000000 --- a/app-modules/activity/tests/Feature/Filament/Admin/Message/CreateMessageTest.php +++ /dev/null @@ -1,46 +0,0 @@ -create(); - $provider = ExternalIdentity::factory() - ->recycle($tenant) - ->create(['provider' => IdentityProvider::Discord]); - $season = Season::factory() - ->recycle($tenant) - ->create(); - - $data = [ - 'tenant_id' => $tenant->getKey(), - 'external_identity_id' => $provider->getKey(), - 'channel_id' => 1, - 'provider_message_id' => 'prov-msg-123', - 'content' => 'Mensagem de teste enviada', - 'sent_at' => now(), - 'obtained_experience' => 50, - ]; - - livewire(CreateMessage::class) - ->fillForm($data) - ->call('create') - ->assertHasNoFormErrors() - ->assertNotified(); - - assertDatabaseHas(Message::class, [ - 'tenant_id' => $tenant->getKey(), - 'external_identity_id' => $provider->getKey(), - 'content' => 'Mensagem de teste enviada', - ]); -}); diff --git a/app-modules/activity/tests/Feature/Filament/Admin/Message/DeleteMessageTest.php b/app-modules/activity/tests/Feature/Filament/Admin/Message/DeleteMessageTest.php deleted file mode 100644 index 94b04915..00000000 --- a/app-modules/activity/tests/Feature/Filament/Admin/Message/DeleteMessageTest.php +++ /dev/null @@ -1,25 +0,0 @@ -create(); - - livewire(EditMessage::class, [ - 'record' => $message->getKey(), - ]) - ->callAction(DeleteAction::class) - ->assertNotified() - ->assertRedirect(); - - assertDatabaseMissing(Message::class, [ - 'id' => $message->getKey(), - ]); -}); diff --git a/app-modules/activity/tests/Feature/Filament/Admin/Message/EditMessageTest.php b/app-modules/activity/tests/Feature/Filament/Admin/Message/EditMessageTest.php deleted file mode 100644 index 735a333a..00000000 --- a/app-modules/activity/tests/Feature/Filament/Admin/Message/EditMessageTest.php +++ /dev/null @@ -1,49 +0,0 @@ -create(); - $season = Season::factory()->create(); - - $newData = [ - 'content' => 'Conteúdo atualizado', - 'obtained_experience' => 200, - ]; - - livewire(EditMessage::class, [ - 'record' => $message->getKey(), - ]) - ->fillForm($newData) - ->call('save') - ->assertHasNoFormErrors() - ->assertNotified(); - - assertDatabaseHas(Message::class, [ - 'id' => $message->getKey(), - 'content' => 'Conteúdo atualizado', - 'obtained_experience' => 200, - ]); -}); - -it('can load the edit page', function (): void { - $message = Message::factory()->create(); - - livewire(EditMessage::class, [ - 'record' => $message->getKey(), - ]) - ->assertOk() - ->assertSchemaStateSet([ - 'external_identity_id' => $message->external_identity_id, - 'channel_id' => $message->channel_id, - 'content' => $message->content, - 'provider_message_id' => $message->provider_message_id, - ]); -}); diff --git a/app-modules/activity/tests/Feature/Filament/Admin/Message/ListMessageTest.phpTest.php b/app-modules/activity/tests/Feature/Filament/Admin/Message/ListMessageTest.phpTest.php deleted file mode 100644 index f2b86b39..00000000 --- a/app-modules/activity/tests/Feature/Filament/Admin/Message/ListMessageTest.phpTest.php +++ /dev/null @@ -1,16 +0,0 @@ -count(5)->create(); - - livewire(ListMessages::class) - ->assertOk() - ->assertCanSeeTableRecords($messages); -}); diff --git a/app-modules/activity/tests/Feature/Filament/Admin/Message/MessageResourceTest.php b/app-modules/activity/tests/Feature/Filament/Admin/Message/MessageResourceTest.php deleted file mode 100644 index 00292547..00000000 --- a/app-modules/activity/tests/Feature/Filament/Admin/Message/MessageResourceTest.php +++ /dev/null @@ -1,11 +0,0 @@ -toContain(MessageResource::class); -}); diff --git a/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/FeedbackResource.php b/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/FeedbackResource.php deleted file mode 100644 index 3c8258b5..00000000 --- a/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/FeedbackResource.php +++ /dev/null @@ -1,48 +0,0 @@ - ListFeedback::route('/'), - 'create' => CreateFeedback::route('/create'), - 'edit' => EditFeedback::route('/{record}/edit'), - ]; - } -} diff --git a/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/Pages/CreateFeedback.php b/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/Pages/CreateFeedback.php deleted file mode 100644 index 7066ecab..00000000 --- a/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/Pages/CreateFeedback.php +++ /dev/null @@ -1,13 +0,0 @@ -components([ - Select::make('sender_id') - ->label('Sender') - ->preload() - ->searchable() - ->relationship( - name: 'sender', - titleAttribute: 'username', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - - Select::make('target_id') - ->label('Target') - ->preload() - ->searchable() - ->relationship( - name: 'target', - titleAttribute: 'username', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - - Select::make('tenant_id') - ->label('Target') - ->preload() - ->searchable() - ->relationship( - name: 'tenant', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - - TextInput::make('type') - ->label('Type') - ->required() - ->maxLength(50), - - Textarea::make('message') - ->label('Message') - ->required() - ->rows(5) - ->maxLength(500), - ]); - } -} diff --git a/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/Tables/FeedbackTable.php b/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/Tables/FeedbackTable.php deleted file mode 100644 index 64113396..00000000 --- a/app-modules/community/src/Feedback/Filament/Admin/Resources/Feedback/Tables/FeedbackTable.php +++ /dev/null @@ -1,56 +0,0 @@ -columns([ - TextColumn::make('sender.username') - ->label('Sender') - ->sortable() - ->searchable(), - - TextColumn::make('target.username') - ->label('Target') - ->sortable() - ->searchable(), - - TextColumn::make('type') - ->label('Type') - ->sortable(), - - TextColumn::make('message') - ->label('Message') - ->limit(50) - ->wrap(), - - TextColumn::make('review.id') - ->label('Review') - ->sortable(), - - TextColumn::make('created_at') - ->label('Created') - ->dateTime('d/m/Y H:i') - ->sortable(), - ]) - ->recordActions([ - EditAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Schemas/MeetingTypeForm.php b/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Schemas/MeetingTypeForm.php deleted file mode 100644 index c9ec4e3b..00000000 --- a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Schemas/MeetingTypeForm.php +++ /dev/null @@ -1,34 +0,0 @@ -components([ - TextInput::make('name') - ->required(), - Select::make('week_day') - ->options([ - 1 => 'Monday', - 2 => 'Tuesday', - 3 => 'Wednesday', - 4 => 'Thursday', - 5 => 'Friday', - 6 => 'Saturday', - 7 => 'Sunday', - ]), - TimePicker::make('start_at') - ->required(), - ]); - } -} diff --git a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Schemas/MeetingForm.php b/app-modules/community/src/Meeting/Filament/Resources/Meetings/Schemas/MeetingForm.php deleted file mode 100644 index 935c2b63..00000000 --- a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Schemas/MeetingForm.php +++ /dev/null @@ -1,91 +0,0 @@ -components([ - Section::make('Administration Area') - ->description('Tenant and Administration') - ->schema([ - Select::make('tenant_id') - ->preload() - ->searchable() - ->relationship( - name: 'tenant', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - Select::make('admin_id') - ->preload() - ->searchable() - ->relationship( - name: 'admin', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - ]), - Section::make('Meeting Data') - ->description('Choose the Start and End') - ->schema([ - TimePicker::make('starts_at') - ->hoursStep(2) - ->required(), - TimePicker::make('ends_at') - ->hoursStep(2) - ->after('starts_at') - ->required(), - ]), - Section::make('Meeting Fields') - ->description('Meeting Region') - ->schema([ - RichEditor::make('content') - ->columnSpanFull(), - Select::make('meeting_type_id') - ->preload() - ->searchable() - ->relationship( - name: 'meetingType', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->createOptionForm([ - TextInput::make('name') - ->minLength(3) - ->maxLength(255) - ->required(), - Select::make('week_day') - ->options([ - 1 => 'Monday', - 2 => 'Tuesday', - 3 => 'Wednesday', - 4 => 'Thursday', - 5 => 'Friday', - 6 => 'Saturday', - 7 => 'Sunday', - ]) - ->required(), - TimePicker::make('start_at') - ->required(), - ]) - ->required(), - ]) - ->columnSpanFull(), - ]); - } -} diff --git a/app-modules/community/src/Providers/CommunityServiceProvider.php b/app-modules/community/src/Providers/CommunityServiceProvider.php index 2e3383b7..ef5c9c0e 100644 --- a/app-modules/community/src/Providers/CommunityServiceProvider.php +++ b/app-modules/community/src/Providers/CommunityServiceProvider.php @@ -4,29 +4,11 @@ namespace He4rt\Community\Providers; -use App\Enums\FilamentPanel; -use Filament\Panel; -use He4rt\Community\Feedback\Filament\Admin\Resources\Feedback\FeedbackResource; -use He4rt\Community\Meeting\Filament\Resources\Meetings\MeetingResource; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\MeetingTypeResource; use Illuminate\Support\ServiceProvider; class CommunityServiceProvider extends ServiceProvider { - public function register(): void - { - Panel::configureUsing(function (Panel $panel): void { - match ($panel->currentPanel()) { - FilamentPanel::Admin => $panel - ->resources([ - MeetingResource::class, - MeetingTypeResource::class, - FeedbackResource::class, - ]), - default => null, - }; - }); - } + public function register(): void {} public function boot(): void { diff --git a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/CreateFeedbackTest.php b/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/CreateFeedbackTest.php deleted file mode 100644 index 6736ddd2..00000000 --- a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/CreateFeedbackTest.php +++ /dev/null @@ -1,35 +0,0 @@ -setCurrentPanel('admin'); - $sender = User::factory()->create(); - $target = User::factory()->create(); - - $tenant = Tenant::factory()->create(); - - $data = [ - 'sender_id' => $sender->getKey(), - 'target_id' => $target->getKey(), - 'tenant_id' => $tenant->getKey(), - 'type' => 'compliment', - 'message' => 'Bom trabalho', - ]; - - livewire(CreateFeedback::class) - ->fillForm($data) - ->call('create') - ->assertHasNoFormErrors() - ->assertNotified(); - - assertDatabaseHas(Feedback::class, $data); -}); diff --git a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/DeleteFeedbackTest.php b/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/DeleteFeedbackTest.php deleted file mode 100644 index b91283ad..00000000 --- a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/DeleteFeedbackTest.php +++ /dev/null @@ -1,26 +0,0 @@ -setCurrentPanel('admin'); - $feedback = Feedback::factory()->create(); - - livewire(EditFeedback::class, [ - 'record' => $feedback->getKey(), - ]) - ->callAction(DeleteAction::class) - ->assertNotified() - ->assertRedirect(); - - assertDatabaseMissing(Feedback::class, [ - 'id' => $feedback->getKey(), - ]); -}); diff --git a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/EditFeedbackTest.php b/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/EditFeedbackTest.php deleted file mode 100644 index c503e938..00000000 --- a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/EditFeedbackTest.php +++ /dev/null @@ -1,50 +0,0 @@ -setCurrentPanel('admin'); - /** @var Feedback $feedback */ - $feedback = Feedback::factory()->create(); - - livewire(EditFeedback::class, [ - 'record' => $feedback->getKey(), - ]) - ->assertOk() - ->assertSchemaStateSet([ - 'sender_id' => $feedback->sender_id, - 'target_id' => $feedback->target_id, - 'type' => $feedback->type, - 'message' => $feedback->message, - ]); -}); - -it('can update feedback', function (): void { - filament()->setCurrentPanel('admin'); - $feedback = Feedback::factory()->create(); - - $newData = [ - 'type' => 'compliment', - 'message' => 'Precisa melhorar', - ]; - - livewire(EditFeedback::class, [ - 'record' => $feedback->getKey(), - ]) - ->fillForm($newData) - ->call('save') - ->assertHasNoFormErrors() - ->assertNotified(); - - assertDatabaseHas(Feedback::class, [ - 'id' => $feedback->getKey(), - 'type' => $newData['type'], - 'message' => $newData['message'], - ]); -}); diff --git a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/FeedbackResourceTest.php b/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/FeedbackResourceTest.php deleted file mode 100644 index 7a7ea8d2..00000000 --- a/app-modules/community/tests/Feature/Feedback/Filament/Admin/Feedback/FeedbackResourceTest.php +++ /dev/null @@ -1,12 +0,0 @@ -setCurrentPanel('admin'); - expect(Filament::getResources()) - ->toContain(FeedbackResource::class); -}); diff --git a/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/CreateMeetingTest.php b/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/CreateMeetingTest.php deleted file mode 100644 index d38998e1..00000000 --- a/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/CreateMeetingTest.php +++ /dev/null @@ -1,52 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - livewire(CreateMeeting::class) - ->assertOk(); -}); - -it('should be able to create a meet', function (): void { - $admin = User::factory()->create(); - $tenant = Tenant::factory()->create(); - $meetingType = MeetingType::factory()->create(); - - livewire(CreateMeeting::class) - ->assertOk() - ->fillForm([ - 'tenant_id' => $tenant->getKey(), - 'admin_id' => $admin->getKey(), - 'content' => 'content', - 'meeting_type_id' => $meetingType->getKey(), - 'starts_at' => Date::yesterday(), - 'ends_at' => Date::tomorrow(), - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(Meeting::class, 1); - assertDatabaseHas(Meeting::class, [ - 'tenant_id' => $tenant->getKey(), - 'admin_id' => $admin->getKey(), - 'meeting_type_id' => $meetingType->getKey(), - ]); -}); diff --git a/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/EditMeetingTest.php b/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/EditMeetingTest.php deleted file mode 100644 index f3fa1f68..00000000 --- a/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/EditMeetingTest.php +++ /dev/null @@ -1,21 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - $meeting = Meeting::factory()->create(); - livewire(EditMeeting::class, ['record' => $meeting->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/ListMeetingTest.php b/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/ListMeetingTest.php deleted file mode 100644 index de28c85f..00000000 --- a/app-modules/community/tests/Feature/Meeting/Filament/Admin/Meeting/ListMeetingTest.php +++ /dev/null @@ -1,19 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - livewire(ListMeetings::class) - ->assertOk(); -}); diff --git a/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/CreateMeetingTypeTest.php b/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/CreateMeetingTypeTest.php deleted file mode 100644 index f17a5897..00000000 --- a/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/CreateMeetingTypeTest.php +++ /dev/null @@ -1,41 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - livewire(CreateMeetingType::class) - ->assertOk(); -}); - -it('should be able to create a meet', function (): void { - livewire(CreateMeetingType::class) - ->assertOk() - ->fillForm([ - 'name' => 'meeting type name', - 'week_day' => 1, - 'start_at' => '01:00:00', - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(MeetingType::class, 1); - assertDatabaseHas(MeetingType::class, [ - 'name' => 'meeting type name', - 'week_day' => 1, - 'start_at' => '01:00:00', - ]); -}); diff --git a/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/EditMeetingTypeTest.php b/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/EditMeetingTypeTest.php deleted file mode 100644 index 5171239e..00000000 --- a/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/EditMeetingTypeTest.php +++ /dev/null @@ -1,21 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - $meetingType = MeetingType::factory()->create(); - livewire(EditMeetingType::class, ['record' => $meetingType->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/ListMeetingTypeTest.php b/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/ListMeetingTypeTest.php deleted file mode 100644 index 33b75053..00000000 --- a/app-modules/community/tests/Feature/Meeting/Filament/Admin/MeetingType/ListMeetingTypeTest.php +++ /dev/null @@ -1,19 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - livewire(ListMeetingTypes::class) - ->assertOk(); -}); diff --git a/app-modules/events/src/AdminEventPanelPlugin.php b/app-modules/events/src/AdminEventPanelPlugin.php deleted file mode 100644 index 5be600f5..00000000 --- a/app-modules/events/src/AdminEventPanelPlugin.php +++ /dev/null @@ -1,34 +0,0 @@ -moduleName('event'); - } - - public function register(Panel $panel): void - { - $panel->resources([ - EventResource::class, - TalkResource::class, - EventAgendaResource::class, - SponsorResource::class, - ]); - - } - - public function boot(Panel $panel): void {} -} diff --git a/app-modules/events/src/Filament/Admin/Resources/EventAgenda/EventAgendaResource.php b/app-modules/events/src/Filament/Admin/Resources/EventAgenda/EventAgendaResource.php deleted file mode 100644 index 836879dd..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/EventAgenda/EventAgendaResource.php +++ /dev/null @@ -1,169 +0,0 @@ -components([ - Select::make('tenant_id') - ->relationship('tenant', 'name') - ->required(), - Select::make('event_id') - ->relationship( - 'event', - 'title', - ) - ->required(), - - MorphToSelect::make('schedulable') - ->types([ - Type::make(EventSubmission::class) - ->titleAttribute('title'), - Type::make(EventSegment::class) - ->titleAttribute('title'), - ]), - - Fieldset::make('Schedule') - ->schema([ - TimePicker::make('starting_at') - ->native(false) - ->required(), - - TimePicker::make('ending_at') - ->native(false) - ->required(), - ]), - - TextEntry::make('created_at') - ->label('Created Date') - ->state(fn (?EventAgenda $record): string => $record?->created_at?->diffForHumans() ?? '-'), - - TextEntry::make('updated_at') - ->label('Last Modified Date') - ->state(fn (?EventAgenda $record): string => $record?->updated_at?->diffForHumans() ?? '-'), - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ - TextColumn::make('tenant.name') - ->searchable() - ->sortable(), - - TextColumn::make('event.title') - ->searchable() - ->sortable(), - - TextColumn::make('schedulable_type'), - - TextColumn::make('schedulable_id'), - - TextColumn::make('starting_at'), - - TextColumn::make('ending_at'), - ]) - ->filters([ - TrashedFilter::make(), - ]) - ->recordActions([ - EditAction::make(), - DeleteAction::make(), - RestoreAction::make(), - ForceDeleteAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - RestoreBulkAction::make(), - ForceDeleteBulkAction::make(), - ]), - ]); - } - - public static function getPages(): array - { - return [ - 'index' => ListEventAgendas::route('/'), - 'create' => CreateEventAgenda::route('/create'), - 'edit' => EditEventAgenda::route('/{record}/edit'), - ]; - } - - public static function getEloquentQuery(): Builder - { - return parent::getEloquentQuery() - ->withoutGlobalScopes([ - SoftDeletingScope::class, - ]); - } - - public static function getGlobalSearchEloquentQuery(): Builder - { - return parent::getGlobalSearchEloquentQuery()->with(['tenant', 'event']); - } - - public static function getGloballySearchableAttributes(): array - { - return ['tenant.name', 'event.title']; - } - - public static function getGlobalSearchResultDetails(Model $record): array - { - /** @var EventAgenda $record */ - $details = []; - - if ($record->tenant) { - $details['Tenant'] = $record->tenant->name; - } - - if ($record->event) { - $details['Event'] = $record->event->title; - } - - return $details; - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/EventAgenda/Pages/CreateEventAgenda.php b/app-modules/events/src/Filament/Admin/Resources/EventAgenda/Pages/CreateEventAgenda.php deleted file mode 100644 index 35e5c7ac..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/EventAgenda/Pages/CreateEventAgenda.php +++ /dev/null @@ -1,20 +0,0 @@ - ListEvents::route('/'), - 'create' => CreateEvent::route('/create'), - 'edit' => EditEvent::route('/{record}/edit'), - ]; - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Events/Pages/CreateEvent.php b/app-modules/events/src/Filament/Admin/Resources/Events/Pages/CreateEvent.php deleted file mode 100644 index dfe24965..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Events/Pages/CreateEvent.php +++ /dev/null @@ -1,13 +0,0 @@ -attendees()->count(); - } - - public static function getIcon(Model $ownerRecord, string $pageClass): string|BackedEnum|null - { - return Heroicon::Users; - } - - public function table(Table $table): Table - { - return $table - ->columns([ - TextColumn::make('username'), - TextColumn::make('email'), - IconColumn::make('is_donator'), - TextColumn::make('pivot.status') - ->label('Status') - ->badge(), - ]) - ->recordActions([ - DetachAction::make() - ->using(function (User $record): void { - /** @var EventModel $ownerRecord */ - $ownerRecord = $this->getOwnerRecord(); - $ownerRecord->leave($record->getKey()); - }), - ]); - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Events/RelationManagers/TalksRelationManager.php b/app-modules/events/src/Filament/Admin/Resources/Events/RelationManagers/TalksRelationManager.php deleted file mode 100644 index 79955d2c..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Events/RelationManagers/TalksRelationManager.php +++ /dev/null @@ -1,40 +0,0 @@ -talks()->count(); - } - - public static function getIcon(Model $ownerRecord, string $pageClass): string|BackedEnum|null - { - return Heroicon::Microphone; - } - - public function table(Table $table): Table - { - return $table - ->headerActions([ - CreateAction::make(), - ]); - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Events/Schemas/EventForm.php b/app-modules/events/src/Filament/Admin/Resources/Events/Schemas/EventForm.php deleted file mode 100644 index 44cc84d1..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Events/Schemas/EventForm.php +++ /dev/null @@ -1,107 +0,0 @@ -components([ - Flex::make([ - Section::make('Informações Gerais') - ->schema([ - Select::make('tenant_id') - ->label('Tenant') - ->relationship('tenant', 'name') - ->required(), - - TextInput::make('title') - ->label('Title') - ->minLength(5) - ->maxLength(255) - ->afterStateUpdated(fn ($state, Set $set) => $set('slug', Str::slug($state))) - ->required(), - - TextInput::make('slug') - ->required(), - - Select::make('event_type') - ->label('Event Type') - ->enum(EventTypeEnum::class) - ->options(EventTypeEnum::class) - ->required(), - ]) - ->columns(2), - ]), - - Flex::make([ - Section::make('Local e Capacidade') - ->schema([ - Grid::make(2)->schema([ - TextInput::make('location') - ->label('Location') - ->minLength(5) - ->maxLength(255) - ->required(), - - TextInput::make('max_attendees') - ->numeric() - ->minValue(1) - ->label('Max Attendees') - ->required(), - ]), - ]), - Section::make('Status') - ->schema([ - Toggle::make('active') - ->label('Active') - ->required(), - ]), - ]), - - Section::make('Datas e Horários') - ->schema([ - Grid::make(3)->schema([ - DateTimePicker::make('event_at') - ->label('Event Date') - ->required(), - - DateTimePicker::make('start_at') - ->label('Start Hour') - ->required(), - - DateTimePicker::make('end_at') - ->label('End Hour') - ->after('start_at') - ->required(), - ]), - ]), - - Section::make('Conteúdo') - ->schema([ - RichEditor::make('description') - ->label('Description') - ->minLength(5) - ->maxLength(255) - ->required(), - ]), - ]) - ->columns(1); - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Events/Tables/EventsTable.php b/app-modules/events/src/Filament/Admin/Resources/Events/Tables/EventsTable.php deleted file mode 100644 index 1061ebea..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Events/Tables/EventsTable.php +++ /dev/null @@ -1,59 +0,0 @@ -columns([ - TextColumn::make('title') - ->limit(20) - ->searchable(), - - TextColumn::make('location') - ->searchable(), - TextColumn::make('event_type') - ->badge() - ->searchable(), - - IconColumn::make('active') - ->sortable() - ->boolean(), - - TextColumn::make('attendees_count') - ->label('Inscritos') - ->sortable() - ->formatStateUsing(fn (string $state, $record) => sprintf('%s/%s', $state, $record->max_attendees)), - - TextColumn::make('talks_count') - ->label('Submissions') - ->sortable() - ->counts('talks'), - - TextColumn::make('event_at') - ->sortable() - ->date(), - - TextColumn::make('start_at') - ->label('EventModel Hour') - ->formatStateUsing(fn ($state) => $state->format('d/m/Y H:i')) - ->description(fn ($record) => $record->end_at->format('d/m/Y H:i')) - ->sortable(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/CreateTalk.php b/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/CreateTalk.php deleted file mode 100644 index c0cf24a4..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/CreateTalk.php +++ /dev/null @@ -1,13 +0,0 @@ -components([ - - Flex::make([ - Section::make('Proposta da Palestra') - ->description('Defina o evento, título e tipo da sua proposta.') - ->icon('heroicon-m-clipboard-document-list') - ->schema([ - TextInput::make('title') - ->label('Título da Proposta') - ->minLength(3) - ->maxlength(255) - ->required() - ->columnSpanFull(), - - Select::make('tenant_id') - ->label('Tenant') - ->relationship('tenant', 'name') - ->required() - ->live() - ->columnSpan(1), - - Select::make('user_id') - ->label('User') - ->relationship('user', 'username') - ->required() - ->live() - ->columnSpan(1), - - TextInput::make('field_type') - ->label('Tipo') - ->minLength(3) - ->maxlength(255) - ->required() - ->columnSpan(1), - - Select::make('status') - ->label('Status') - ->options(TalkStatusEnum::class) - ->required() - ->default(TalkStatusEnum::Pending), - ]) - ->columnSpan(2), - ]) - ->columnSpanFull(), - - Section::make('Detalhes e Conteúdo') - ->description('Forneça a descrição completa da sua palestra e o que o público aprenderá.') - ->icon('heroicon-m-document-text') - ->schema([ - RichEditor::make('description') - ->label('Descrição Completa') - ->required() - ->columnSpanFull(), - ]) - ->columnSpanFull(), - - ]); - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Talks/Tables/TalksTable.php b/app-modules/events/src/Filament/Admin/Resources/Talks/Tables/TalksTable.php deleted file mode 100644 index 4a3b7ee1..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Talks/Tables/TalksTable.php +++ /dev/null @@ -1,40 +0,0 @@ -columns([ - TextColumn::make('title') - ->description(fn ($record) => $record->user->name) - ->searchable(), - TextColumn::make('description') - ->limit(30) - ->searchable(), - TextColumn::make('field_type') - ->searchable(), - TextColumn::make('status') - ->badge() - ->searchable(), - ]) - ->recordActions([ - EditAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/events/src/Filament/Admin/Resources/Talks/TalkResource.php b/app-modules/events/src/Filament/Admin/Resources/Talks/TalkResource.php deleted file mode 100644 index 353b809c..00000000 --- a/app-modules/events/src/Filament/Admin/Resources/Talks/TalkResource.php +++ /dev/null @@ -1,55 +0,0 @@ - ListTalks::route('/'), - 'create' => CreateTalk::route('/create'), - 'edit' => EditTalk::route('/{record}/edit'), - ]; - } -} diff --git a/app-modules/events/src/Filament/Resources/EventSubmissionSpeakerResource.php b/app-modules/events/src/Filament/Resources/EventSubmissionSpeakerResource.php deleted file mode 100644 index f66b07d9..00000000 --- a/app-modules/events/src/Filament/Resources/EventSubmissionSpeakerResource.php +++ /dev/null @@ -1,136 +0,0 @@ -components([ - Select::make('event_id') - ->relationship('event', 'title') - ->searchable() - ->required(), - - Select::make('user_id') - ->relationship('user', 'name') - ->searchable() - ->required(), - - TextEntry::make('created_at') - ->label('Created Date') - ->state(fn (?EventSubmissionSpeaker $record): string => $record?->created_at?->diffForHumans() ?? '-'), - - TextEntry::make('updated_at') - ->label('Last Modified Date') - ->state(fn (?EventSubmissionSpeaker $record): string => $record?->updated_at?->diffForHumans() ?? '-'), - ]); - } - - public static function table(Table $table): Table - { - return $table - ->columns([ - TextColumn::make('event.title') - ->searchable() - ->sortable(), - - TextColumn::make('user.name') - ->searchable() - ->sortable(), - ]) - ->filters([ - TrashedFilter::make(), - ]) - ->recordActions([ - EditAction::make(), - DeleteAction::make(), - RestoreAction::make(), - ForceDeleteAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - RestoreBulkAction::make(), - ForceDeleteBulkAction::make(), - ]), - ]); - } - - public static function getPages(): array - { - return [ - 'index' => ListEventSubmissionSpeakers::route('/'), - 'create' => CreateEventSubmissionSpeaker::route('/create'), - 'edit' => EditEventSubmissionSpeaker::route('/{record}/edit'), - ]; - } - - public static function getEloquentQuery(): Builder - { - return parent::getEloquentQuery() - ->withoutGlobalScopes([ - SoftDeletingScope::class, - ]); - } - - public static function getGlobalSearchEloquentQuery(): Builder - { - return parent::getGlobalSearchEloquentQuery()->with(['submission.event', 'user']); - } - - public static function getGloballySearchableAttributes(): array - { - return ['submission.event.title', 'user.name']; - } - - public static function getGlobalSearchResultDetails(Model $record): array - { - /** @var EventSubmissionSpeaker $record */ - $details = []; - - if ($record->submission?->event) { - $details['Event'] = $record->submission->event->title; - } - - if ($record->user) { - $details['User'] = $record->user->name; - } - - return $details; - } -} diff --git a/app-modules/events/src/Filament/Resources/EventSubmissionSpeakerResource/Pages/CreateEventSubmissionSpeaker.php b/app-modules/events/src/Filament/Resources/EventSubmissionSpeakerResource/Pages/CreateEventSubmissionSpeaker.php deleted file mode 100644 index 93f9c9cb..00000000 --- a/app-modules/events/src/Filament/Resources/EventSubmissionSpeakerResource/Pages/CreateEventSubmissionSpeaker.php +++ /dev/null @@ -1,20 +0,0 @@ -components([ - Section::make('Sponsors') - ->description('Sponsors Information') - ->schema([ - Select::make('tenant_id') - ->label('Tenant') - ->preload() - ->searchable() - ->relationship( - name: 'tenant', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - TextInput::make('name') - ->minLength(5) - ->maxLength(255) - ->required(), - SpatieMediaLibraryFileUpload::make('receipt') - ->label('Sponsor Logo') - ->collection('receipt') - ->image() - ->required(), - TextInput::make('homepage_url') - ->url(), - ]) - ->columnSpanFull(), - ]); - } -} diff --git a/app-modules/events/src/Models/EventModel.php b/app-modules/events/src/Models/EventModel.php index 920bbdf1..44387e8e 100644 --- a/app-modules/events/src/Models/EventModel.php +++ b/app-modules/events/src/Models/EventModel.php @@ -11,6 +11,7 @@ use He4rt\Events\Enums\EventTypeEnum; use He4rt\Events\Enums\Talks\TalkStatusEnum; use He4rt\Events\Models\Pivot\EventAttend; +use He4rt\Events\Models\Pivot\SponsorAttend; use He4rt\Identity\Tenant\Models\Tenant; use He4rt\Identity\User\Models\User; use Illuminate\Database\Eloquent\Attributes\Scope; @@ -178,6 +179,17 @@ public function agenda(): HasMany ->oldest('starting_at'); } + /** + * @return BelongsToMany + */ + public function sponsors(): BelongsToMany + { + return $this->belongsToMany(Sponsor::class, 'events_sponsors', 'event_id', 'sponsor_id') + ->using(SponsorAttend::class) + ->withPivot(['level']) + ->withTimestamps(); + } + /** @return Attribute */ protected function duration(): Attribute { diff --git a/app-modules/events/src/Providers/EventsServiceProvider.php b/app-modules/events/src/Providers/EventsServiceProvider.php index 46279d25..17688f79 100644 --- a/app-modules/events/src/Providers/EventsServiceProvider.php +++ b/app-modules/events/src/Providers/EventsServiceProvider.php @@ -6,7 +6,6 @@ use App\Enums\FilamentPanel; use Filament\Panel; -use He4rt\Events\AdminEventPanelPlugin; use He4rt\Events\AppEventPanelPlugin; use He4rt\Events\EventPanelPlugin; use He4rt\Events\Models\EventSegment; @@ -21,7 +20,6 @@ public function register(): void { Panel::configureUsing(function (Panel $panel): void { match ($panel->currentPanel()) { - FilamentPanel::Admin => $panel->plugin(new AdminEventPanelPlugin), FilamentPanel::User => $panel->plugin(new AppEventPanelPlugin), FilamentPanel::Event => $panel->plugin(new EventPanelPlugin()), default => null, diff --git a/app-modules/events/tests/Feature/Filament/Admin/Event/AttendeesRelationManagerTest.php b/app-modules/events/tests/Feature/Filament/Admin/Event/AttendeesRelationManagerTest.php deleted file mode 100644 index 41d2affd..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Event/AttendeesRelationManagerTest.php +++ /dev/null @@ -1,120 +0,0 @@ -user = User::factory()->create(); - actingAs($this->user); - $this->tenant = Tenant::factory()->create(); - $this->event = EventModel::factory()->recycle($this->tenant)->create(); - Filament::setCurrentPanel('admin'); -}); - -it('should render', function (): void { - livewire(AttendeesRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ])->assertOk(); -}); - -it('should list event attendees', function (): void { - $attendees = User::factory()->count(5)->create(); - - foreach ($attendees as $attendee) { - $this->event->attendees()->attach($attendee->getKey(), [ - 'status' => AttendingStatusEnum::Attending, - ]); - } - - livewire(AttendeesRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertCanSeeTableRecords($attendees) - ->assertCountTableRecords(5); -}); - -it('should display attendee columns correctly', function (): void { - $attendee = User::factory()->create([ - 'username' => 'johndoe', - 'email' => 'john@example.com', - 'is_donator' => true, - ]); - - $this->event->attendees()->attach($attendee->getKey(), [ - 'status' => AttendingStatusEnum::Attending, - ]); - - livewire(AttendeesRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertCanSeeTableRecords([$attendee]) - ->assertTableColumnExists('username') - ->assertTableColumnExists('email') - ->assertTableColumnExists('is_donator') - ->assertTableColumnExists('pivot.status'); -}); - -it('should detach attendee using leave method', function (): void { - $attendee = User::factory()->create(); - - $this->event->attendees()->attach($attendee->getKey(), [ - 'status' => AttendingStatusEnum::Attending, - ]); - - $this->event->increment('attendees_count'); - - expect($this->event->fresh()->attendees_count)->toBe(1) - ->and($this->event->attendees()->count())->toBe(1); - - TestAction::make(DetachAction::class)->table(); - - livewire(AttendeesRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertCanSeeTableRecords([$attendee]) - ->callTableAction('detach', $attendee); - - expect($this->event->fresh()->attendees_count)->toBe(0) - ->and($this->event->attendees()->count())->toBe(0); -}); - -it('should decrement waitlist_count when detaching waitlisted attendee', function (): void { - $attendee = User::factory()->create(); - - $this->event->attendees()->attach($attendee->getKey(), [ - 'status' => AttendingStatusEnum::Waitlist, - ]); - $this->event->increment('waitlist_count'); - - expect($this->event->fresh()->waitlist_count)->toBe(1); - - TestAction::make(DetachAction::class)->table(); - - livewire(AttendeesRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->callTableAction('detach', $attendee); - - expect($this->event->fresh()->waitlist_count)->toBe(0) - ->and($this->event->attendees()->count())->toBe(0); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Event/CreateEventTest.php b/app-modules/events/tests/Feature/Filament/Admin/Event/CreateEventTest.php deleted file mode 100644 index 5d6b942b..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Event/CreateEventTest.php +++ /dev/null @@ -1,165 +0,0 @@ -value); - $this->actingAsAdmin(); - $this->tenant = Tenant::query()->first(); -}); - -it('should render', function (): void { - livewire(CreateEvent::class) - ->assertOk(); -}); - -it('should be able to create an event', function (): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'tenant_id' => $this->tenant->getKey(), - 'title' => 'event title', - 'slug' => 'event-slug', - 'description' => 'event description', - 'location' => 'event location', - 'max_attendees' => 5, - 'event_type' => EventTypeEnum::Workshop->value, - 'active' => true, - 'event_at' => today(), - 'start_at' => today(), - 'end_at' => Date::tomorrow(), - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(EventModel::class, 1); - assertDatabaseHas(EventModel::class, [ - 'tenant_id' => $this->tenant->getKey(), - 'title' => 'event title', - 'slug' => 'event-slug', - 'location' => 'event location', - 'max_attendees' => 5, - 'event_type' => EventTypeEnum::Workshop->value, - 'active' => true, - 'event_at' => today(), - 'start_at' => today(), - 'end_at' => Date::tomorrow(), - ]); -}); - -describe('validation tests', function (): void { - - test('title::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'title' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['title' => $rule]); - })->with([ - 'required' => ['', 'required'], - 'min' => ['aa', 'min:5'], - 'max' => [str_repeat('a', 256), 'max:255'], - ]); - test('location::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'location' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['location' => $rule]); - })->with([ - 'required' => ['', 'required'], - 'min' => ['aa', 'min:5'], - 'max' => [str_repeat('a', 256), 'max:255'], - ]); - - test('event_type::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'event_type' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['event_type' => $rule]); - })->with([ - 'required' => ['', 'required'], - 'enum' => ['aa', 'The selected event Type is invalid.'], - ]); - - test('max_attendees::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'max_attendees' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['max_attendees' => $rule]); - })->with([ - 'required' => [null, 'required'], - 'min 1' => [-1, 'min'], - ]); - - test('active::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'active' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['active' => $rule]); - })->with([ - 'required' => [null, 'required'], - ]); - test('event_at::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'event_at' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['event_at' => $rule]); - })->with([ - 'required' => [null, 'required'], - ]); - - test('start_at::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'start_at' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['start_at' => $rule]); - })->with([ - 'required' => [null, 'required'], - ]); - - test('end_at::validations', function ($value, $rule): void { - livewire(CreateEvent::class) - ->assertOk() - ->fillForm([ - 'start_at' => now(), - 'end_at' => $value, - ]) - ->call('create') - ->assertHasFormErrors(['end_at' => $rule]); - })->with([ - 'required' => [null, 'required'], - 'after' => [Date::yesterday(), 'after'], - ]); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Event/EditEventTest.php b/app-modules/events/tests/Feature/Filament/Admin/Event/EditEventTest.php deleted file mode 100644 index 12ba92a8..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Event/EditEventTest.php +++ /dev/null @@ -1,18 +0,0 @@ -value); - $event = EventModel::factory()->createOne(); - $this->actingAsAdmin(); - livewire(EditEvent::class, ['record' => $event->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Event/ListEventsTest.php b/app-modules/events/tests/Feature/Filament/Admin/Event/ListEventsTest.php deleted file mode 100644 index 6db39eb7..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Event/ListEventsTest.php +++ /dev/null @@ -1,16 +0,0 @@ -value); - $this->actingAsAdmin(); - livewire(ListEvents::class) - ->assertOk(); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Event/TalkRelationManagerTest.php b/app-modules/events/tests/Feature/Filament/Admin/Event/TalkRelationManagerTest.php deleted file mode 100644 index 3d05d301..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Event/TalkRelationManagerTest.php +++ /dev/null @@ -1,179 +0,0 @@ -user = User::factory()->create(); - actingAs($this->user); - $this->tenant = Tenant::factory()->create(); - $this->event = EventModel::factory()->recycle($this->tenant)->create(); - Filament::setCurrentPanel('admin'); -}); - -it('should render', function (): void { - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ])->assertOk(); -}); - -it('should list event talks', function (): void { - $talks = EventSubmission::factory() - ->recycle($this->event) - ->recycle($this->tenant) - ->count(5) - ->create(); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertCanSeeTableRecords($talks) - ->assertCountTableRecords(5); -}); - -it('should have create action', function (): void { - $action = TestAction::make(CreateAction::class)->table(); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertActionExists($action); -}); - -it('should create a talk for the event', function (): void { - $speaker = User::factory()->create(); - $action = TestAction::make(CreateAction::class)->table(); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->mountAction($action) - ->fillForm([ - 'user_id' => $speaker->getKey(), - 'tenant_id' => $this->tenant->getKey(), - 'title' => 'Introduction to Laravel', - 'description' => 'A beginner friendly talk about Laravel', - 'status' => TalkStatusEnum::Accepted->value, - 'field_type' => 'talk', - 'starts_at' => $this->event->start_at, - 'ends_at' => $this->event->start_at->addHour(), - ]) - ->callMountedAction() - ->assertHasNoFormErrors() - ->assertCountTableRecords(1); - - $talk = $this->event->fresh()->talks()->first(); - - expect($talk) - ->title->toBe('Introduction to Laravel') - ->user_id->toBe($speaker->getKey()) - ->event_id->toBe($this->event->getKey()) - ->tenant_id->toBe($this->tenant->getKey()) - ->status->toBe(TalkStatusEnum::Accepted); -}); - -it('should associate talk with event automatically', function (): void { - $speaker = User::factory()->create(); - $action = TestAction::make(CreateAction::class)->table(); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->mountAction($action) - ->fillForm([ - 'user_id' => $speaker->getKey(), - 'tenant_id' => $this->tenant->getKey(), - 'title' => 'My EventSubmission', - 'description' => '

EventSubmission description

', - 'status' => TalkStatusEnum::Pending->value, - 'field_type' => 'talk', - 'starts_at' => $this->event->start_at, - 'ends_at' => $this->event->start_at->addHour(), - ]) - ->callMountedAction(); - - $talk = EventSubmission::query()->first(); - - expect($talk->event_id)->toBe($this->event->getKey()); -}); - -it('should validate required fields when creating talk', function (): void { - $action = TestAction::make(CreateAction::class)->table(); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->mountAction($action) - ->fillForm([ - 'title' => '', - 'description' => '', - ]) - ->callMountedAction() - ->assertHasFormErrors(['title', 'user_id']); -}); - -it('should list talks with different statuses', function (): void { - $pendingTalk = EventSubmission::factory() - ->recycle($this->event) - ->recycle($this->tenant) - ->create(['status' => TalkStatusEnum::Pending]); - - $acceptedTalk = EventSubmission::factory() - ->recycle($this->event) - ->recycle($this->tenant) - ->create(['status' => TalkStatusEnum::Accepted]); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertCanSeeTableRecords([$pendingTalk, $acceptedTalk]) - ->assertCountTableRecords(2); -}); - -it('should only show talks belonging to the event', function (): void { - $eventTalks = EventSubmission::factory() - ->recycle($this->event) - ->recycle($this->tenant) - ->count(3) - ->create(); - - $otherEvent = EventModel::factory()->recycle($this->tenant)->create(); - $otherTalks = EventSubmission::factory() - ->recycle($otherEvent) - ->recycle($this->tenant) - ->count(2) - ->create(); - - livewire(TalksRelationManager::class, [ - 'ownerRecord' => $this->event, - 'pageClass' => EditEvent::class, - ]) - ->assertOk() - ->assertCanSeeTableRecords($eventTalks) - ->assertCanNotSeeTableRecords($otherTalks) - ->assertCountTableRecords(3); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Sponsors/CreateSponsorTest.php b/app-modules/events/tests/Feature/Filament/Admin/Sponsors/CreateSponsorTest.php deleted file mode 100644 index 4bb0f05e..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Sponsors/CreateSponsorTest.php +++ /dev/null @@ -1,54 +0,0 @@ -value); - $this->actingAsAdmin(); - - livewire(CreateSponsor::class) - ->assertOk(); -}); - -it('should be able to register a sponsor', function (): void { - Storage::fake('public'); - $image = UploadedFile::fake()->image('image.jpg'); - Filament::setCurrentPanel(FilamentPanel::Admin->value); - $this->actingAsAdmin(); - - $tenant = Tenant::query()->first(); - - livewire(CreateSponsor::class) - ->assertOk() - ->fillForm([ - 'tenant_id' => $tenant->getKey(), - 'name' => 'sponsor name', - 'receipt' => $image, - 'homepage_url' => 'https://www.google.com', - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(Sponsor::class, 1); - - assertDatabaseHas(Sponsor::class, [ - 'tenant_id' => $tenant->getKey(), - 'name' => 'sponsor name', - 'homepage_url' => 'https://www.google.com', - ]); - - $sponsor = Sponsor::query()->first(); - expect(count($sponsor->getMediaRepository()->all()))->toBe(1); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Sponsors/EditSponsorTest.php b/app-modules/events/tests/Feature/Filament/Admin/Sponsors/EditSponsorTest.php deleted file mode 100644 index 677b6442..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Sponsors/EditSponsorTest.php +++ /dev/null @@ -1,19 +0,0 @@ -create(); - Filament::setCurrentPanel(FilamentPanel::Admin->value); - $this->actingAsAdmin(); - - livewire(EditSponsor::class, ['record' => $sponsor->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Sponsors/ListSponsorsTest.php b/app-modules/events/tests/Feature/Filament/Admin/Sponsors/ListSponsorsTest.php deleted file mode 100644 index a2995a4d..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Sponsors/ListSponsorsTest.php +++ /dev/null @@ -1,17 +0,0 @@ -value); - $this->actingAsAdmin(); - - livewire(ListSponsors::class) - ->assertOk(); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Talk/CreateTalk.php b/app-modules/events/tests/Feature/Filament/Admin/Talk/CreateTalk.php deleted file mode 100644 index 9576ee2e..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Talk/CreateTalk.php +++ /dev/null @@ -1,55 +0,0 @@ -value); - $this->actingAsAdmin(); - livewire(CreateTalk::class) - ->assertOk(); -}); - -it('should be able to register a talk', function (): void { - Filament::setCurrentPanel(FilamentPanel::Admin->value); - $this->actingAsAdmin(); - $event = EventModel::factory()->create(); - $user = User::factory()->create(); - livewire(CreateTalk::class) - ->assertOk() - ->fillForm([ - 'tenant_id' => 1, - 'user_id' => $user->getKey(), - 'event_id' => $event->getKey(), - 'title' => 'talk title', - 'status' => TalkStatusEnum::Pending->value, - 'field_type' => 'some text right there', - 'description' => 'description you know', - 'starts_at' => now(), - 'ends_at' => now()->addHour(), - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(EventSubmission::class, 1); - assertDatabaseHas(EventSubmission::class, [ - 'tenant_id' => 1, - 'user_id' => $user->getKey(), - 'event_id' => $event->getKey(), - 'title' => 'talk title', - 'status' => TalkStatusEnum::Pending->value, - 'field_type' => 'some text right there', - 'description' => '

description you know

', - ]); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Talk/EditTalk.php b/app-modules/events/tests/Feature/Filament/Admin/Talk/EditTalk.php deleted file mode 100644 index 405b4c48..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Talk/EditTalk.php +++ /dev/null @@ -1,18 +0,0 @@ -create(); - Filament::setCurrentPanel(FilamentPanel::Admin->value); - $this->actingAsAdmin(); - - livewire(EditTalk::class, ['record' => $talk->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/events/tests/Feature/Filament/Admin/Talk/ListTalks.php b/app-modules/events/tests/Feature/Filament/Admin/Talk/ListTalks.php deleted file mode 100644 index a6b7d8b8..00000000 --- a/app-modules/events/tests/Feature/Filament/Admin/Talk/ListTalks.php +++ /dev/null @@ -1,15 +0,0 @@ -value); - $this->actingAsAdmin(); - livewire(ListTalks::class) - ->assertOk(); -}); diff --git a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Schemas/BadgeForm.php b/app-modules/gamification/src/Badge/Filament/Resources/Badges/Schemas/BadgeForm.php deleted file mode 100644 index 07752500..00000000 --- a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Schemas/BadgeForm.php +++ /dev/null @@ -1,64 +0,0 @@ -components([ - Section::make() - ->description('Badge Information') - ->icon(Heroicon::Tag) - ->schema([ - Section::make() - ->description('Administration Area') - ->schema([ - Select::make('tenant_id') - ->preload() - ->searchable() - ->relationship( - name: 'tenant', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - Select::make('provider') - ->enum(IdentityProvider::class) - ->options(IdentityProvider::class) - ->required(), - Toggle::make('active') - ->required(), - TextInput::make('redeem_code') - ->required(), - ]), - TextInput::make('name') - ->minLength(3) - ->maxLength(255) - ->required(), - RichEditor::make('description') - ->required() - ->columnSpanFull(), - SpatieMediaLibraryFileUpload::make('badge') - ->collection('badge') - ->image() - ->required(), - ]) - ->columnSpanFull(), - ]); - } -} diff --git a/app-modules/gamification/src/Badge/Models/Badge.php b/app-modules/gamification/src/Badge/Models/Badge.php index 389031be..04badd3a 100644 --- a/app-modules/gamification/src/Badge/Models/Badge.php +++ b/app-modules/gamification/src/Badge/Models/Badge.php @@ -4,12 +4,15 @@ namespace He4rt\Gamification\Badge\Models; +use He4rt\Gamification\Character\Models\Character; use He4rt\Gamification\Database\Factories\BadgeFactory; use He4rt\Identity\ExternalIdentity\Enums\IdentityProvider; use He4rt\Identity\Tenant\Models\Tenant; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\Pivot; use Spatie\MediaLibrary\HasMedia; use Spatie\MediaLibrary\InteractsWithMedia; @@ -39,6 +42,19 @@ public function tenant(): BelongsTo return $this->belongsTo(Tenant::class); } + /** + * @return BelongsToMany + */ + public function characters(): BelongsToMany + { + return $this->belongsToMany( + Character::class, + 'characters_badges', + 'badge_id', + 'character_id' + )->withPivot(['claimed_at']); + } + public function registerMediaCollections(): void { $this->addMediaCollection('badge') diff --git a/app-modules/gamification/src/Character/Models/PastSeason.php b/app-modules/gamification/src/Character/Models/PastSeason.php index 86780bb2..8aef97c7 100644 --- a/app-modules/gamification/src/Character/Models/PastSeason.php +++ b/app-modules/gamification/src/Character/Models/PastSeason.php @@ -23,6 +23,7 @@ final class PastSeason extends Model 'season_id', 'character_id', 'ranking_position', + 'level', 'experience', 'messages_count', 'badges_count', diff --git a/app-modules/gamification/src/Providers/GamificationServiceProvider.php b/app-modules/gamification/src/Providers/GamificationServiceProvider.php index 3e9a4ad6..531d3eca 100644 --- a/app-modules/gamification/src/Providers/GamificationServiceProvider.php +++ b/app-modules/gamification/src/Providers/GamificationServiceProvider.php @@ -4,34 +4,14 @@ namespace He4rt\Gamification\Providers; -use App\Enums\FilamentPanel; -use Filament\Panel; -use He4rt\Gamification\Badge\Filament\Resources\Badges\BadgeResource; use He4rt\Gamification\Badge\Models\Badge; use He4rt\Gamification\Character\Models\Character; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\SeasonResource; -use He4rt\Gamification\Season\Filament\Shared\Widgets\SeasonStatsOverview; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\ServiceProvider; class GamificationServiceProvider extends ServiceProvider { - public function register(): void - { - Panel::configureUsing(function (Panel $panel): void { - match ($panel->currentPanel()) { - FilamentPanel::Admin => $panel - ->resources([ - BadgeResource::class, - SeasonResource::class, - ]) - ->widgets([ - SeasonStatsOverview::class, - ]), - default => null, - }; - }); - } + public function register(): void {} public function boot(): void { diff --git a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/EditSeason.php b/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/EditSeason.php deleted file mode 100644 index 3a3acb7e..00000000 --- a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/EditSeason.php +++ /dev/null @@ -1,21 +0,0 @@ -components([ - Select::make('tenant_id') - ->preload() - ->searchable() - ->relationship( - name: 'tenant', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - TextInput::make('name') - ->required(), - RichEditor::make('description') - ->required() - ->columnSpanFull(), - DateTimePicker::make('started_at'), - DateTimePicker::make('ended_at') - ->after('started_at'), - TextInput::make('messages_count') - ->required() - ->numeric() - ->default(0), - TextInput::make('participants_count') - ->required() - ->numeric() - ->default(0), - TextInput::make('meeting_count') - ->required() - ->numeric() - ->default(0), - TextInput::make('badges_count') - ->required() - ->numeric() - ->default(0), - ]); - } -} diff --git a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Tables/SeasonsTable.php b/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Tables/SeasonsTable.php deleted file mode 100644 index 94bbc8c6..00000000 --- a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Tables/SeasonsTable.php +++ /dev/null @@ -1,59 +0,0 @@ -columns([ - TextColumn::make('id') - ->label('ID') - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('name') - ->searchable(), - TextColumn::make('started_at') - ->label('Season') - ->formatStateUsing(fn ($state) => $state->format('d/m/Y H:i')) - ->description(fn ($record) => $record->ended_at?->format('d/m/Y H:i')) - ->sortable(), - TextColumn::make('messages_count') - ->numeric() - ->sortable(), - TextColumn::make('participants_count') - ->numeric() - ->sortable(), - TextColumn::make('meeting_count') - ->numeric() - ->sortable(), - TextColumn::make('badges_count') - ->numeric() - ->sortable(), - TextColumn::make('created_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('updated_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - ]) - ->recordActions([ - EditAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/gamification/src/Season/Filament/Shared/Widgets/SeasonStatsOverview.php b/app-modules/gamification/src/Season/Filament/Shared/Widgets/SeasonStatsOverview.php deleted file mode 100644 index b13c634e..00000000 --- a/app-modules/gamification/src/Season/Filament/Shared/Widgets/SeasonStatsOverview.php +++ /dev/null @@ -1,55 +0,0 @@ -where('started_at', '<=', now()) - ->where(fn (Builder $q) => $q->whereNull('ended_at') - ->orWhere('ended_at', '>', now()) - ) - ->latest('started_at') - ->first(); - - if (! $season) { - return [ - Stat::make('Season ativa', 'Nenhuma') - ->description('Não há season em andamento') - ->color('gray'), - ]; - } - - return [ - Stat::make('Season ativa', $season->name) - ->description( - $season->ended_at - ? 'Finalizada em '.$season->ended_at->diffForHumans() - : 'Iniciada em '.$season->started_at->diffForHumans() - ) - ->descriptionIcon('heroicon-o-flag') - ->color($season->ended_at ? 'gray' : 'primary'), - - Stat::make('Mensagens processadas', $season->messages_count) - ->description('Total de mensagens registradas') - ->descriptionIcon('heroicon-o-chat-bubble-left-right') - ->color('success'), - - Stat::make('Participantes', $season->participants_count) - ->description('Usuários ativos na season') - ->descriptionIcon('heroicon-o-users') - ->color('warning'), - ]; - } -} diff --git a/app-modules/gamification/src/Season/Models/Season.php b/app-modules/gamification/src/Season/Models/Season.php index ba01d7c3..45f99bd6 100644 --- a/app-modules/gamification/src/Season/Models/Season.php +++ b/app-modules/gamification/src/Season/Models/Season.php @@ -6,6 +6,7 @@ use He4rt\Community\Meeting\Models\Meeting; use He4rt\Gamification\Badge\Models\Badge; +use He4rt\Gamification\Character\Models\PastSeason; use He4rt\Gamification\Database\Factories\SeasonFactory; use He4rt\Identity\Tenant\Models\Tenant; use Illuminate\Database\Eloquent\Concerns\HasUuids; @@ -62,6 +63,14 @@ public function meetings(): HasMany return $this->hasMany(Meeting::class); } + /** + * @return HasMany + */ + public function rankings(): HasMany + { + return $this->hasMany(PastSeason::class, 'season_id'); + } + /** * @return BelongsTo */ diff --git a/app-modules/gamification/tests/Feature/Badge/Filament/Admin/CreateBadgeTest.php b/app-modules/gamification/tests/Feature/Badge/Filament/Admin/CreateBadgeTest.php deleted file mode 100644 index 0045c670..00000000 --- a/app-modules/gamification/tests/Feature/Badge/Filament/Admin/CreateBadgeTest.php +++ /dev/null @@ -1,59 +0,0 @@ -createOne()); -}); - -it('should render', function (): void { - livewire(CreateBadge::class) - ->assertOk(); -}); - -it('should be able to create a badge', function (): void { - Storage::fake('public'); - $image = UploadedFile::fake()->image('image.jpg'); - $tenant = Tenant::factory()->create(); - - livewire(CreateBadge::class) - ->assertOk() - ->fillForm([ - 'tenant_id' => $tenant->getKey(), - 'provider' => IdentityProvider::Discord, - 'name' => 'name', - 'description' => 'description', - 'badge' => $image, - 'redeem_code' => 'redeem_code', - 'active' => true, - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(Badge::class, 1); - assertDatabaseHas(Badge::class, [ - 'tenant_id' => $tenant->getKey(), - 'provider' => IdentityProvider::Discord->value, - 'name' => 'name', - 'redeem_code' => 'redeem_code', - 'active' => 1, - ]); - $badge = Badge::query()->first(); - - expect(count($badge->getMediaRepository()->all()))->toBe(1); -}); diff --git a/app-modules/gamification/tests/Feature/Badge/Filament/Admin/EditBadgeTest.php b/app-modules/gamification/tests/Feature/Badge/Filament/Admin/EditBadgeTest.php deleted file mode 100644 index 55bcbe56..00000000 --- a/app-modules/gamification/tests/Feature/Badge/Filament/Admin/EditBadgeTest.php +++ /dev/null @@ -1,20 +0,0 @@ -createOne()); - $badge = Badge::factory()->create(); - - livewire(EditBadge::class, ['record' => $badge->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/gamification/tests/Feature/Badge/Filament/Admin/ListBadgeTest.php b/app-modules/gamification/tests/Feature/Badge/Filament/Admin/ListBadgeTest.php deleted file mode 100644 index 7cb4f6c4..00000000 --- a/app-modules/gamification/tests/Feature/Badge/Filament/Admin/ListBadgeTest.php +++ /dev/null @@ -1,17 +0,0 @@ -createOne()); - livewire(ListBadges::class) - ->assertOk(); -}); diff --git a/app-modules/gamification/tests/Feature/Season/Filament/Admin/CreateSeasonTest.php b/app-modules/gamification/tests/Feature/Season/Filament/Admin/CreateSeasonTest.php deleted file mode 100644 index 32cda1d2..00000000 --- a/app-modules/gamification/tests/Feature/Season/Filament/Admin/CreateSeasonTest.php +++ /dev/null @@ -1,55 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - livewire(CreateSeason::class) - ->assertOk(); -}); - -it('should be able to create a new season', function (): void { - $tenant = Tenant::factory()->create(); - livewire(CreateSeason::class) - ->assertOk() - ->fillForm([ - 'tenant_id' => $tenant->getKey(), - 'name' => 'season 1', - 'description' => 'description da season', - 'started_at' => Date::yesterday(), - 'ended_at' => Date::tomorrow(), - 'messages_count' => 5, - 'participants_count' => 5, - 'meeting_count' => 5, - 'badges_count' => 5, - ]) - ->call('create') - ->assertHasNoFormErrors(); - - assertDatabaseCount(Season::class, 1); - assertDatabaseHas(Season::class, [ - 'tenant_id' => $tenant->getKey(), - 'name' => 'season 1', - 'started_at' => Date::yesterday(), - 'ended_at' => Date::tomorrow(), - 'messages_count' => 5, - 'participants_count' => 5, - 'meeting_count' => 5, - 'badges_count' => 5, - ]); -}); diff --git a/app-modules/gamification/tests/Feature/Season/Filament/Admin/EditSeasonTest.php b/app-modules/gamification/tests/Feature/Season/Filament/Admin/EditSeasonTest.php deleted file mode 100644 index 9af8440e..00000000 --- a/app-modules/gamification/tests/Feature/Season/Filament/Admin/EditSeasonTest.php +++ /dev/null @@ -1,21 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - $season = Season::factory()->create(); - livewire(EditSeason::class, ['record' => $season->getKey()]) - ->assertOk(); -}); diff --git a/app-modules/gamification/tests/Feature/Season/Filament/Admin/ListSeasonTest.php b/app-modules/gamification/tests/Feature/Season/Filament/Admin/ListSeasonTest.php deleted file mode 100644 index 8493f0ee..00000000 --- a/app-modules/gamification/tests/Feature/Season/Filament/Admin/ListSeasonTest.php +++ /dev/null @@ -1,19 +0,0 @@ -value); - $this->actingAsAdmin(); -}); - -it('should render', function (): void { - livewire(ListSeasons::class) - ->assertOk(); -}); diff --git a/app-modules/identity/database/migrations/2026_03_22_000001_add_index_to_messages_external_identity_id.php b/app-modules/identity/database/migrations/2026_03_22_000001_add_index_to_messages_external_identity_id.php new file mode 100644 index 00000000..11988284 --- /dev/null +++ b/app-modules/identity/database/migrations/2026_03_22_000001_add_index_to_messages_external_identity_id.php @@ -0,0 +1,24 @@ +index('external_identity_id'); + }); + } + + public function down(): void + { + Schema::table('messages', function (Blueprint $table): void { + $table->dropIndex(['external_identity_id']); + }); + } +}; diff --git a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/EditTenant.php b/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/EditTenant.php deleted file mode 100644 index 233d55be..00000000 --- a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/EditTenant.php +++ /dev/null @@ -1,21 +0,0 @@ -components([ - TextInput::make('username') - ->required(), - TextInput::make('name') - ->required(), - TextInput::make('email') - ->label('Email address') - ->email(), - TextInput::make('password') - ->password(), - Toggle::make('is_donator') - ->required(), - ]); - } - - public function infolist(Schema $schema): Schema - { - return $schema - ->components([ - TextEntry::make('id') - ->label('ID'), - TextEntry::make('username'), - TextEntry::make('name'), - TextEntry::make('email') - ->label('Email address') - ->placeholder('-'), - IconEntry::make('is_donator') - ->boolean(), - TextEntry::make('created_at') - ->dateTime() - ->placeholder('-'), - TextEntry::make('updated_at') - ->dateTime() - ->placeholder('-'), - ]); - } - - public function table(Table $table): Table - { - return $table - ->recordTitleAttribute('name') - ->columns([ - TextColumn::make('id') - ->label('ID') - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('username') - ->searchable(), - TextColumn::make('name') - ->searchable(), - TextColumn::make('email') - ->label('Email address') - ->searchable(), - IconColumn::make('is_donator') - ->boolean(), - TextColumn::make('created_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('updated_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - ]) - ->headerActions([ - AttachAction::make(), - ]) - ->recordActions([ - ViewAction::make(), - EditAction::make(), - DetachAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DetachBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Schemas/TenantForm.php b/app-modules/identity/src/Filament/Admin/Resources/Tenants/Schemas/TenantForm.php deleted file mode 100644 index c2dca3e5..00000000 --- a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Schemas/TenantForm.php +++ /dev/null @@ -1,52 +0,0 @@ -components([ - Section::make() - ->schema([ - TextInput::make('name') - ->minLength(3) - ->maxLength(255) - ->afterStateUpdated(fn ($state, Set $set) => $set('slug', Str::slug($state))) - ->live(debounce: 500) - ->required(), - TextInput::make('domain') - ->required(), - TextInput::make('slug') - ->readonly() - ->partiallyRenderComponentsAfterStateUpdated(['name']) - ->required(), - Select::make('owner_id') - ->native(false) - ->searchable() - ->preload() - ->relationship( - name: 'owner', - titleAttribute: 'name', - modifyQueryUsing: fn (Builder $query) => $query->limit(10) - ) - ->required(), - Toggle::make('active') - ->required(), - ]) - ->columnSpanFull(), - ]); - } -} diff --git a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Tables/TenantsTable.php b/app-modules/identity/src/Filament/Admin/Resources/Tenants/Tables/TenantsTable.php deleted file mode 100644 index 8e8f31e2..00000000 --- a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Tables/TenantsTable.php +++ /dev/null @@ -1,38 +0,0 @@ -columns([ - TextColumn::make('name') - ->searchable(), - TextColumn::make('slug') - ->searchable(), - TextColumn::make('owner.name') - ->searchable(), - IconColumn::make('active') - ->boolean(), - ]) - ->recordActions([ - EditAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/EditUser.php b/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/EditUser.php deleted file mode 100644 index 3e9c14d8..00000000 --- a/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/EditUser.php +++ /dev/null @@ -1,108 +0,0 @@ -components([ - Tabs::make('UserTabs') - ->tabs([ - Tab::make('General') - ->schema([ - Section::make('General Information') - ->description('Basic user details') - ->schema(fn (Schema $schema) => UserForm::configure($schema)), - ]), - - Tab::make('Address') - ->schema(fn (Schema $schema) => UserAddressForm::form($schema)), - - Tab::make('Information') - ->schema([ - Section::make('Information') - ->description('Additional profile information') - ->relationship('information') - ->schema(fn (Schema $schema) => UserInformationForm::configure($schema)), - ]), - - Tab::make('Gamefication') - ->schema([ - Section::make('Gamefication') - ->description('Player and progression data') - ->schema([ - TextEntry::make('character.tenant.name') - ->label('Tenant'), - - TextEntry::make('character.reputation') - ->label('Reputation'), - - TextEntry::make('character.experience') - ->label('Experience'), - - TextEntry::make('character.daily_bonus_claimed_at') - ->label('Daily Bonus Claimed At') - ->formatStateUsing(fn ($state - ) => $state ? Date::parse($state)->format('Y-m-d') : ''), - ])->columns(4), - ]), - Tab::make('Providers') - ->schema([ - Section::make('Providers') - ->description('Connected OAuth providers') - ->schema([ - Repeater::make('providers') - ->relationship('providers') - ->schema([ - TextEntry::make('provider') - ->label('Provider'), - - TextEntry::make('external_account_id') - ->label('External Account ID'), - - TextEntry::make('metadata.email') - ->label('Email'), - - TextEntry::make('metadata.username') - ->label('Username'), - - TextEntry::make('connected_at') - ->label('Connected At') - ->dateTime(), - ]) - ->columns(3) - ->addable(false) - ->deletable(false), - ]), - ]), - ]) - ->columnSpan('full'), - ]); - } - - protected function getHeaderActions(): array - { - return [ - DeleteAction::make(), - ]; - } -} diff --git a/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/ListUsers.php b/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/ListUsers.php deleted file mode 100644 index 52906acf..00000000 --- a/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/ListUsers.php +++ /dev/null @@ -1,29 +0,0 @@ -components([ - TextInput::make('username') - ->label('Nome de usuário') - ->placeholder('ex.: joao123') - ->required() - ->minLength(3) - ->maxLength(50), - - TextInput::make('name') - ->label('Nome completo') - ->placeholder('Nome e sobrenome') - ->required() - ->maxLength(100), - - TextInput::make('email') - ->label('E-mail') - ->email() - ->placeholder('ex.: usuario@dominio.com') - ->required() - ->maxLength(255), - - TextInput::make('password') - ->label('Senha') - ->password() - ->placeholder('Mínimo de 8 caracteres') - ->required() - ->minLength(8), - ]); - } -} diff --git a/app-modules/identity/src/Filament/Admin/Resources/Users/UserResource.php b/app-modules/identity/src/Filament/Admin/Resources/Users/UserResource.php deleted file mode 100644 index 304ed5ee..00000000 --- a/app-modules/identity/src/Filament/Admin/Resources/Users/UserResource.php +++ /dev/null @@ -1,58 +0,0 @@ -remember('total_users', minutes(5), fn () => self::$model::count()) ?? 0)); - } - - public static function form(Schema $schema): Schema - { - return UserForm::configure($schema); - } - - public static function table(Table $table): Table - { - return UsersTable::configure($table); - } - - public static function getPages(): array - { - return [ - 'index' => ListUsers::route('/'), - 'create' => CreateUser::route('/create'), - 'edit' => EditUser::route('/{record}/edit'), - ]; - } -} diff --git a/app-modules/identity/src/Filament/Shared/Widgets/UsersStatsOverview.php b/app-modules/identity/src/Filament/Shared/Widgets/UsersStatsOverview.php deleted file mode 100644 index f2250986..00000000 --- a/app-modules/identity/src/Filament/Shared/Widgets/UsersStatsOverview.php +++ /dev/null @@ -1,51 +0,0 @@ -remember('total_users_donators', minutes(5), fn () => $query - ->where('is_donator', true) - ->count()); - - $totalUsersToday = cache()->remember('total_users_today', minutes(5), fn () => $query - ->whereDate('created_at', today()) - ->count()); - - $totalUsersMonth = cache()->remember('total_users_month', minutes(5), fn () => $query - ->whereYear('created_at', now()->year) - ->whereMonth('created_at', now()->month) - ->count()); - - return [ - Stat::make('Usuários criados hoje', $totalUsersToday) - ->description('Novos usuários nas últimas 24h') - ->descriptionIcon('heroicon-o-user-plus') - ->color('success'), - - Stat::make('Usuários criados este mês', $totalUsersMonth) - ->description('Total no mês atual') - ->descriptionIcon('heroicon-o-calendar') - ->color('primary'), - - Stat::make('Usuários doadores', $totalDonators) - ->description('Usuários com doação ativa') - ->descriptionIcon('heroicon-o-heart') - ->color('warning'), - ]; - } -} diff --git a/app-modules/identity/src/Providers/IdentityServiceProvider.php b/app-modules/identity/src/Providers/IdentityServiceProvider.php index 77c8c6a8..0f5cb8a6 100644 --- a/app-modules/identity/src/Providers/IdentityServiceProvider.php +++ b/app-modules/identity/src/Providers/IdentityServiceProvider.php @@ -6,9 +6,6 @@ use App\Enums\FilamentPanel; use Filament\Panel; -use He4rt\Identity\Filament\Admin\Resources\Tenants\TenantResource; -use He4rt\Identity\Filament\Admin\Resources\Users\UserResource; -use He4rt\Identity\Filament\Shared\Widgets\UsersStatsOverview; use He4rt\Identity\Filament\User\Pages\Dashboard; use He4rt\Identity\Filament\User\Pages\UserProfile; use He4rt\Identity\Tenant\Models\Tenant; @@ -22,14 +19,6 @@ public function register(): void { Panel::configureUsing(function (Panel $panel): void { match ($panel->currentPanel()) { - FilamentPanel::Admin => $panel - ->resources([ - UserResource::class, - TenantResource::class, - ]) - ->widgets([ - UsersStatsOverview::class, - ]), FilamentPanel::User => $panel ->pages([ UserProfile::class, diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/CreateTenantTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/CreateTenantTest.php deleted file mode 100644 index 5b5ef91b..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/CreateTenantTest.php +++ /dev/null @@ -1,62 +0,0 @@ -assertOk(); -}); - -it('can create a tenant', function (): void { - $owner = User::factory()->create(); - - livewire(CreateTenant::class) - ->fillForm([ - 'name' => 'My Tenant', - 'slug' => 'my-tenant', - 'domain' => 'my-tenant.test', - 'owner_id' => $owner->id, - 'active' => true, - ]) - ->call('create') - ->assertNotified() - ->assertRedirect(); - - $this->assertDatabaseHas(Tenant::class, [ - 'name' => 'My Tenant', - 'slug' => 'my-tenant', - 'domain' => 'my-tenant.test', - 'owner_id' => $owner->id, - 'active' => true, - ]); -}); - -test('name field should fill slug after updated ', function (): void { - $owner = User::factory()->create(); - - livewire(CreateTenant::class) - ->fillForm([ - 'name' => 'My Tenant', - 'owner_id' => $owner->id, - 'domain' => 'my-tenant.test', - 'active' => true, - ]) - ->assertSchemaComponentStateSet('slug', 'my-tenant') - ->call('create') - ->assertNotified() - ->assertRedirect(); - - $this->assertDatabaseHas(Tenant::class, [ - 'name' => 'My Tenant', - 'slug' => 'my-tenant', - 'domain' => 'my-tenant.test', - 'owner_id' => $owner->id, - 'active' => true, - ]); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/EditTenantTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/EditTenantTest.php deleted file mode 100644 index 5c1510ab..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/EditTenantTest.php +++ /dev/null @@ -1,61 +0,0 @@ -create(); - - livewire(EditTenant::class, [ - 'record' => $tenant->id, - ]) - ->assertOk() - ->assertSchemaStateSet([ - 'name' => $tenant->name, - 'slug' => $tenant->slug, - 'owner_id' => $tenant->owner_id, - 'active' => $tenant->active, - ]); -}); - -it('can update a tenant', function (): void { - $tenant = Tenant::factory()->create(); - $newTenantData = Tenant::factory()->make(); - - livewire(EditTenant::class, [ - 'record' => $tenant->id, - ]) - ->fillForm([ - 'name' => $newTenantData->name, - 'slug' => $newTenantData->slug, - 'domain' => 'my-tenant.test', - 'active' => $newTenantData->active, - ]) - ->call('save') - ->assertNotified(); - - assertDatabaseHas(Tenant::class, [ - 'id' => $tenant->id, - 'name' => $newTenantData->name, - 'slug' => $newTenantData->slug, - ]); -}); - -it('can delete a tenant', function (): void { - $tenant = Tenant::factory()->create(); - - livewire(EditTenant::class, [ - 'record' => $tenant->id, - ]) - ->callAction(DeleteAction::class) - ->assertNotified() - ->assertRedirect(); - - $this->assertSoftDeleted($tenant); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/ListTenantTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/ListTenantTest.php deleted file mode 100644 index 42925acb..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/ListTenantTest.php +++ /dev/null @@ -1,16 +0,0 @@ -count(5)->create(); - - livewire(ListTenants::class) - ->assertOk() - ->assertCanSeeTableRecords($tenants); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/RelationManagers/EventsRelationManagerTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/RelationManagers/EventsRelationManagerTest.php deleted file mode 100644 index 34336c24..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/RelationManagers/EventsRelationManagerTest.php +++ /dev/null @@ -1,33 +0,0 @@ -user = User::factory()->create(); - actingAs($this->user); - $this->tenant = Tenant::factory()->create(); - Filament::setCurrentPanel('admin'); -}); - -it('should render', function (): void { - livewire(EventsRelationManager::class, ['ownerRecord' => $this->tenant, 'pageClass' => EditTenant::class]) - ->assertOk(); -}); - -it('should list the tenant events', function (): void { - $events = EventModel::factory()->recycle($this->tenant)->count(5)->create(); - livewire(EventsRelationManager::class, ['ownerRecord' => $this->tenant, 'pageClass' => EditTenant::class]) - ->assertOk() - ->assertCanSeeTableRecords($events) - ->assertCountTableRecords($events->count()); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/RelationManagers/MembersRelationManagerTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/RelationManagers/MembersRelationManagerTest.php deleted file mode 100644 index d3bddd58..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/RelationManagers/MembersRelationManagerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -user = User::factory()->create(); - actingAs($this->user); - $this->tenant = Tenant::factory()->create(); - Filament::setCurrentPanel('admin'); -}); - -it('should render', function (): void { - livewire(MembersRelationManager::class, ['ownerRecord' => $this->tenant, 'pageClass' => EditTenant::class]) - ->assertOk(); -}); - -it('should list the tenant members', function (): void { - $users = User::factory()->count(5)->create(); - $this->tenant->members()->attach($users->pluck('id')); - livewire(MembersRelationManager::class, ['ownerRecord' => $this->tenant, 'pageClass' => EditTenant::class]) - ->assertOk() - ->assertCanSeeTableRecords($users) - ->assertCountTableRecords($users->count()); -}); -it('should be able to detach an user', function (): void { - $users = User::factory()->count(5)->create(); - $this->tenant->members()->attach($users->pluck('id')); - - $action = TestAction::make(DetachAction::class)->table(); - livewire(MembersRelationManager::class, ['ownerRecord' => $this->tenant, 'pageClass' => EditTenant::class]) - ->assertOk() - ->selectTableRecords($users->pluck('id')->toArray()) - ->callAction($action->bulk()) - ->assertHasNoFormErrors() - ->assertCountTableRecords(0); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/TenantResourceTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/TenantResourceTest.php deleted file mode 100644 index 33c2a1bd..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/Tenant/TenantResourceTest.php +++ /dev/null @@ -1,11 +0,0 @@ -toContain(TenantResource::class); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/CreateUserTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/CreateUserTest.php deleted file mode 100644 index 24c76853..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/CreateUserTest.php +++ /dev/null @@ -1,30 +0,0 @@ -livewire(CreateUser::class) - ->assertOk(); -}); - -it('can create a user', function (): void { - $this->livewire(CreateUser::class) - ->fillForm([ - 'username' => 'newuser', - 'name' => 'New User', - 'email' => 'emailmaneiro@example.com', - 'password' => 'password', - ]) - ->call('create') - ->assertNotified() - ->assertRedirect(); - - $this->assertDatabaseHas(User::class, [ - 'username' => 'newuser', - 'name' => 'New User', - 'email' => 'emailmaneiro@example.com', - ]); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/EditUserTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/EditUserTest.php deleted file mode 100644 index 1f2937be..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/EditUserTest.php +++ /dev/null @@ -1,55 +0,0 @@ -create(); - - $this->livewire(EditUser::class, [ - 'record' => $user->id, - ]) - ->assertOk() - ->assertSchemaStateSet([ - 'username' => $user->username, - 'email' => $user->email, - ]); -}); - -it('can update a user', function (): void { - $user = User::factory()->create(); - - $this->livewire(EditUser::class, [ - 'record' => $user->id, - ]) - ->fillForm([ - 'username' => 'fulano', - 'email' => 'fulano@email.com', - 'zip_code' => '13000-000', - ]) - ->call('save') - ->assertHasNoFormErrors() - ->assertNotified(); - - $this->assertDatabaseHas(User::class, [ - 'id' => $user->id, - 'username' => 'fulano', - 'email' => 'fulano@email.com', - ]); -}); - -it('can delete a user', function (): void { - $user = User::factory()->create(); - - $this->livewire(EditUser::class, [ - 'record' => $user->id, - ]) - ->callAction(DeleteAction::class) - ->assertNotified() - ->assertRedirect(); - - $this->assertDatabaseMissing($user); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/ListUsersTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/ListUsersTest.php deleted file mode 100644 index f768a36b..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/ListUsersTest.php +++ /dev/null @@ -1,14 +0,0 @@ -count(5)->create(); - - $this->livewire(ListUsers::class) - ->assertOk() - ->assertCanSeeTableRecords($users); -}); diff --git a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/UserResourceTest.php b/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/UserResourceTest.php deleted file mode 100644 index 76d12fac..00000000 --- a/app-modules/identity/tests/Feature/Filament/Admin/Resources/User/UserResourceTest.php +++ /dev/null @@ -1,11 +0,0 @@ -toContain(UserResource::class); -}); diff --git a/app-modules/panel-admin/composer.json b/app-modules/panel-admin/composer.json new file mode 100644 index 00000000..8f53ae64 --- /dev/null +++ b/app-modules/panel-admin/composer.json @@ -0,0 +1,24 @@ +{ + "name": "he4rt/panel-admin", + "description": "", + "type": "library", + "version": "1.0", + "license": "proprietary", + "require": {}, + "autoload": { + "psr-4": { + "He4rt\\PanelAdmin\\": "src/", + "He4rt\\PanelAdmin\\Tests\\": "tests/", + "He4rt\\PanelAdmin\\Database\\Factories\\": "database/factories/", + "He4rt\\PanelAdmin\\Database\\Seeders\\": "database/seeders/" + } + }, + "minimum-stability": "stable", + "extra": { + "laravel": { + "providers": [ + "He4rt\\PanelAdmin\\PanelAdminServiceProvider" + ] + } + } +} diff --git a/app-modules/panel-admin/database/factories/.gitkeep b/app-modules/panel-admin/database/factories/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app-modules/panel-admin/database/migrations/.gitkeep b/app-modules/panel-admin/database/migrations/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app-modules/panel-admin/database/migrations/2026_03_22_224033_set_up_panel-admin_module.php b/app-modules/panel-admin/database/migrations/2026_03_22_224033_set_up_panel-admin_module.php new file mode 100644 index 00000000..78da6aaf --- /dev/null +++ b/app-modules/panel-admin/database/migrations/2026_03_22_224033_set_up_panel-admin_module.php @@ -0,0 +1,23 @@ +bigIncrements('id'); + // $table->timestamps(); + // $table->softDeletes(); + // }); + } + + public function down(): void + { + // Don't listen to the haters + // Schema::dropIfExists('panel-admin'); + } +}; diff --git a/app-modules/panel-admin/database/seeders/.gitkeep b/app-modules/panel-admin/database/seeders/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/app-modules/panel-admin/resources/views/create.blade.php b/app-modules/panel-admin/resources/views/create.blade.php new file mode 100644 index 00000000..fba286cf --- /dev/null +++ b/app-modules/panel-admin/resources/views/create.blade.php @@ -0,0 +1,4 @@ +@extends('layouts.app') + +@section('content') +@endsection diff --git a/app-modules/panel-admin/resources/views/edit.blade.php b/app-modules/panel-admin/resources/views/edit.blade.php new file mode 100644 index 00000000..fba286cf --- /dev/null +++ b/app-modules/panel-admin/resources/views/edit.blade.php @@ -0,0 +1,4 @@ +@extends('layouts.app') + +@section('content') +@endsection diff --git a/app-modules/panel-admin/resources/views/index.blade.php b/app-modules/panel-admin/resources/views/index.blade.php new file mode 100644 index 00000000..fba286cf --- /dev/null +++ b/app-modules/panel-admin/resources/views/index.blade.php @@ -0,0 +1,4 @@ +@extends('layouts.app') + +@section('content') +@endsection diff --git a/app-modules/panel-admin/resources/views/show.blade.php b/app-modules/panel-admin/resources/views/show.blade.php new file mode 100644 index 00000000..fba286cf --- /dev/null +++ b/app-modules/panel-admin/resources/views/show.blade.php @@ -0,0 +1,4 @@ +@extends('layouts.app') + +@section('content') +@endsection diff --git a/app-modules/panel-admin/routes/panel-admin-routes.php b/app-modules/panel-admin/routes/panel-admin-routes.php new file mode 100644 index 00000000..70fe9d3c --- /dev/null +++ b/app-modules/panel-admin/routes/panel-admin-routes.php @@ -0,0 +1,13 @@ +name('panel-admins.index'); +// Route::get('/panel-admins/create', [PanelAdminController::class, 'create'])->name('panel-admins.create'); +// Route::post('/panel-admins', [PanelAdminController::class, 'store'])->name('panel-admins.store'); +// Route::get('/panel-admins/{panel-admin}', [PanelAdminController::class, 'show'])->name('panel-admins.show'); +// Route::get('/panel-admins/{panel-admin}/edit', [PanelAdminController::class, 'edit'])->name('panel-admins.edit'); +// Route::put('/panel-admins/{panel-admin}', [PanelAdminController::class, 'update'])->name('panel-admins.update'); +// Route::delete('/panel-admins/{panel-admin}', [PanelAdminController::class, 'destroy'])->name('panel-admins.destroy'); diff --git a/app-modules/gamification/src/Badge/Filament/Resources/Badges/BadgeResource.php b/app-modules/panel-admin/src/Filament/Resources/Badges/BadgeResource.php similarity index 60% rename from app-modules/gamification/src/Badge/Filament/Resources/Badges/BadgeResource.php rename to app-modules/panel-admin/src/Filament/Resources/Badges/BadgeResource.php index 5be65e61..605439dc 100644 --- a/app-modules/gamification/src/Badge/Filament/Resources/Badges/BadgeResource.php +++ b/app-modules/panel-admin/src/Filament/Resources/Badges/BadgeResource.php @@ -2,28 +2,30 @@ declare(strict_types=1); -namespace He4rt\Gamification\Badge\Filament\Resources\Badges; +namespace He4rt\PanelAdmin\Filament\Resources\Badges; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; -use He4rt\Gamification\Badge\Filament\Resources\Badges\Pages\CreateBadge; -use He4rt\Gamification\Badge\Filament\Resources\Badges\Pages\EditBadge; -use He4rt\Gamification\Badge\Filament\Resources\Badges\Pages\ListBadges; -use He4rt\Gamification\Badge\Filament\Resources\Badges\Schemas\BadgeForm; -use He4rt\Gamification\Badge\Filament\Resources\Badges\Tables\BadgesTable; use He4rt\Gamification\Badge\Models\Badge; +use He4rt\PanelAdmin\Filament\Resources\Badges\Pages\CreateBadge; +use He4rt\PanelAdmin\Filament\Resources\Badges\Pages\EditBadge; +use He4rt\PanelAdmin\Filament\Resources\Badges\Pages\ListBadges; +use He4rt\PanelAdmin\Filament\Resources\Badges\Schemas\BadgeForm; +use He4rt\PanelAdmin\Filament\Resources\Badges\Tables\BadgesTable; use UnitEnum; class BadgeResource extends Resource { protected static ?string $model = Badge::class; - protected static string|UnitEnum|null $navigationGroup = 'Gamefication'; + protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedStar; - protected static string|BackedEnum|null $navigationIcon = Heroicon::Tag; + protected static string|null|UnitEnum $navigationGroup = 'Gamification'; + + protected static ?int $navigationSort = 2; protected static ?string $recordTitleAttribute = 'name'; @@ -37,6 +39,13 @@ public static function table(Table $table): Table return BadgesTable::configure($table); } + public static function getRelations(): array + { + return [ + + ]; + } + public static function getPages(): array { return [ diff --git a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/CreateBadge.php b/app-modules/panel-admin/src/Filament/Resources/Badges/Pages/CreateBadge.php similarity index 57% rename from app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/CreateBadge.php rename to app-modules/panel-admin/src/Filament/Resources/Badges/Pages/CreateBadge.php index 8010389a..2d5b7260 100644 --- a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/CreateBadge.php +++ b/app-modules/panel-admin/src/Filament/Resources/Badges/Pages/CreateBadge.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Gamification\Badge\Filament\Resources\Badges\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Badges\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Gamification\Badge\Filament\Resources\Badges\BadgeResource; +use He4rt\PanelAdmin\Filament\Resources\Badges\BadgeResource; class CreateBadge extends CreateRecord { diff --git a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/EditBadge.php b/app-modules/panel-admin/src/Filament/Resources/Badges/Pages/EditBadge.php similarity index 70% rename from app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/EditBadge.php rename to app-modules/panel-admin/src/Filament/Resources/Badges/Pages/EditBadge.php index 15604c81..fb6d6f53 100644 --- a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/EditBadge.php +++ b/app-modules/panel-admin/src/Filament/Resources/Badges/Pages/EditBadge.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Gamification\Badge\Filament\Resources\Badges\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Badges\Pages; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Gamification\Badge\Filament\Resources\Badges\BadgeResource; +use He4rt\PanelAdmin\Filament\Resources\Badges\BadgeResource; class EditBadge extends EditRecord { diff --git a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/ListBadges.php b/app-modules/panel-admin/src/Filament/Resources/Badges/Pages/ListBadges.php similarity index 71% rename from app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/ListBadges.php rename to app-modules/panel-admin/src/Filament/Resources/Badges/Pages/ListBadges.php index f58f59b5..ae3e886a 100644 --- a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Pages/ListBadges.php +++ b/app-modules/panel-admin/src/Filament/Resources/Badges/Pages/ListBadges.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Gamification\Badge\Filament\Resources\Badges\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Badges\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Gamification\Badge\Filament\Resources\Badges\BadgeResource; +use He4rt\PanelAdmin\Filament\Resources\Badges\BadgeResource; class ListBadges extends ListRecords { diff --git a/app-modules/panel-admin/src/Filament/Resources/Badges/Schemas/BadgeForm.php b/app-modules/panel-admin/src/Filament/Resources/Badges/Schemas/BadgeForm.php new file mode 100644 index 00000000..71091046 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Badges/Schemas/BadgeForm.php @@ -0,0 +1,51 @@ +columns(1) + ->components([ + Select::make('tenant_id') + ->required() + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + + TextInput::make('name') + ->required() + ->maxLength(255), + + Textarea::make('description') + ->nullable() + ->rows(3), + + TextInput::make('redeem_code') + ->required(), + + Select::make('provider') + ->required() + ->options(IdentityProvider::class), + + Toggle::make('active') + ->default(true), + + SpatieMediaLibraryFileUpload::make('badge_image') + ->collection('badge') + ->disk('public'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Badges/Tables/BadgesTable.php b/app-modules/panel-admin/src/Filament/Resources/Badges/Tables/BadgesTable.php new file mode 100644 index 00000000..c0579daf --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Badges/Tables/BadgesTable.php @@ -0,0 +1,70 @@ +columns([ + SpatieMediaLibraryImageColumn::make('badge_image') + ->collection('badge') + ->circular() + ->label('Image'), + + TextColumn::make('name') + ->searchable() + ->sortable(), + + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + + TextColumn::make('redeem_code') + ->copyable(), + + TextColumn::make('provider') + ->badge(), + + IconColumn::make('active') + ->boolean(), + + TextColumn::make('characters_count') + ->counts('characters') + ->label('Claimed'), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + + TernaryFilter::make('active'), + + SelectFilter::make('provider') + ->options(IdentityProvider::class), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Characters/CharacterResource.php b/app-modules/panel-admin/src/Filament/Resources/Characters/CharacterResource.php new file mode 100644 index 00000000..51d2c26e --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Characters/CharacterResource.php @@ -0,0 +1,56 @@ + ListCharacters::route('/'), + 'edit' => EditCharacter::route('/{record}/edit'), + ]; + } +} diff --git a/app-modules/activity/src/Filament/Admin/Resources/Messages/Pages/EditMessage.php b/app-modules/panel-admin/src/Filament/Resources/Characters/Pages/EditCharacter.php similarity index 51% rename from app-modules/activity/src/Filament/Admin/Resources/Messages/Pages/EditMessage.php rename to app-modules/panel-admin/src/Filament/Resources/Characters/Pages/EditCharacter.php index 1067aab3..f758c0be 100644 --- a/app-modules/activity/src/Filament/Admin/Resources/Messages/Pages/EditMessage.php +++ b/app-modules/panel-admin/src/Filament/Resources/Characters/Pages/EditCharacter.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace He4rt\Activity\Filament\Admin\Resources\Messages\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Characters\Pages; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Activity\Filament\Admin\Resources\Messages\MessageResource; +use He4rt\PanelAdmin\Filament\Resources\Characters\CharacterResource; -class EditMessage extends EditRecord +class EditCharacter extends EditRecord { - protected static string $resource = MessageResource::class; + protected static string $resource = CharacterResource::class; protected function getHeaderActions(): array { diff --git a/app-modules/panel-admin/src/Filament/Resources/Characters/Pages/ListCharacters.php b/app-modules/panel-admin/src/Filament/Resources/Characters/Pages/ListCharacters.php new file mode 100644 index 00000000..e82c097f --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Characters/Pages/ListCharacters.php @@ -0,0 +1,13 @@ +columns([ + TextColumn::make('name') + ->searchable(), + + TextColumn::make('provider') + ->badge(), + + TextColumn::make('claimed_at') + ->dateTime() + ->label('Claimed'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Characters/RelationManagers/PastSeasonsRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Characters/RelationManagers/PastSeasonsRelationManager.php new file mode 100644 index 00000000..338628da --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Characters/RelationManagers/PastSeasonsRelationManager.php @@ -0,0 +1,35 @@ +columns([ + TextColumn::make('season.name'), + + TextColumn::make('ranking_position') + ->sortable() + ->label('#'), + + TextColumn::make('experience') + ->numeric(0), + + TextColumn::make('messages_count') + ->numeric(0), + + TextColumn::make('badges_count') + ->numeric(0), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Characters/Schemas/CharacterForm.php b/app-modules/panel-admin/src/Filament/Resources/Characters/Schemas/CharacterForm.php new file mode 100644 index 00000000..33b97b09 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Characters/Schemas/CharacterForm.php @@ -0,0 +1,38 @@ +columns(1) + ->components([ + Select::make('user_id') + ->relationship('user', 'username') + ->disabled() + ->dehydrated(false), + + Select::make('tenant_id') + ->relationship('tenant', 'name') + ->disabled() + ->dehydrated(false), + + TextInput::make('experience') + ->required() + ->integer() + ->minValue(0), + + TextInput::make('reputation') + ->required() + ->integer(), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Characters/Tables/CharactersTable.php b/app-modules/panel-admin/src/Filament/Resources/Characters/Tables/CharactersTable.php new file mode 100644 index 00000000..37095aae --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Characters/Tables/CharactersTable.php @@ -0,0 +1,97 @@ +columns([ + TextColumn::make('user.username') + ->searchable() + ->label('User'), + + TextColumn::make('tenant.name') + ->badge() + ->color('gray') + ->label('Tenant'), + + TextColumn::make('level'), + + TextColumn::make('experience') + ->numeric(0) + ->sortable(), + + TextColumn::make('reputation') + ->numeric(0) + ->sortable(), + + TextColumn::make('daily_bonus_claimed_at') + ->dateTime() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + ]) + ->recordActions([ + EditAction::make(), + Action::make('grantXp') + ->label('Grant XP') + ->icon(Heroicon::ArrowTrendingUp) + ->color('success') + ->form([ + TextInput::make('amount') + ->required() + ->integer() + ->minValue(1), + ]) + ->action(fn ($record, array $data) => $record->increment('experience', (int) $data['amount'])) + ->successNotificationTitle('XP granted successfully'), + Action::make('resetDailyBonus') + ->label('Reset Daily Bonus') + ->icon(Heroicon::ArrowPath) + ->color('warning') + ->requiresConfirmation() + ->action(fn ($record) => $record->update(['daily_bonus_claimed_at' => null])) + ->successNotificationTitle('Daily bonus reset'), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + BulkAction::make('grantXpBulk') + ->label('Grant XP') + ->icon(Heroicon::ArrowTrendingUp) + ->color('success') + ->form([ + TextInput::make('amount') + ->required() + ->integer() + ->minValue(1), + ]) + ->action(function (Collection $records, array $data): void { + $records->each(fn ($record) => $record->increment('experience', (int) $data['amount'])); + }) + ->deselectRecordsAfterCompletion() + ->successNotificationTitle('XP granted to selected characters'), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventModels/EventModelResource.php b/app-modules/panel-admin/src/Filament/Resources/EventModels/EventModelResource.php new file mode 100644 index 00000000..c6f1ca9c --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventModels/EventModelResource.php @@ -0,0 +1,66 @@ + ListEventModels::route('/'), + 'create' => CreateEventModel::route('/create'), + 'edit' => EditEventModel::route('/{record}/edit'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventModels/Pages/CreateEventModel.php b/app-modules/panel-admin/src/Filament/Resources/EventModels/Pages/CreateEventModel.php new file mode 100644 index 00000000..b02c66ad --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventModels/Pages/CreateEventModel.php @@ -0,0 +1,13 @@ +columns([ + TextColumn::make('schedulable_type') + ->badge() + ->formatStateUsing(fn ($state) => class_basename($state)), + TextColumn::make('starting_at') + ->sortable(), + TextColumn::make('ending_at'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventModels/RelationManagers/AttendeesRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/EventModels/RelationManagers/AttendeesRelationManager.php new file mode 100644 index 00000000..4499f360 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventModels/RelationManagers/AttendeesRelationManager.php @@ -0,0 +1,28 @@ +columns([ + TextColumn::make('username') + ->searchable(), + TextColumn::make('status') + ->badge(), + TextColumn::make('attend_order') + ->sortable() + ->label('#'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventModels/RelationManagers/TalksRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/EventModels/RelationManagers/TalksRelationManager.php new file mode 100644 index 00000000..057f2505 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventModels/RelationManagers/TalksRelationManager.php @@ -0,0 +1,45 @@ +columns([ + TextColumn::make('title') + ->searchable(), + TextColumn::make('user.username') + ->label('Speaker'), + TextColumn::make('status') + ->badge(), + TextColumn::make('field_type'), + ]) + ->actions([ + Action::make('accept') + ->icon(Heroicon::Check) + ->color('success') + ->visible(fn ($record) => $record->status === TalkStatusEnum::Pending) + ->action(fn ($record) => $record->update(['status' => TalkStatusEnum::Accepted])) + ->successNotificationTitle('Talk accepted'), + Action::make('reject') + ->icon(Heroicon::XMark) + ->color('danger') + ->visible(fn ($record) => $record->status === TalkStatusEnum::Pending) + ->action(fn ($record) => $record->update(['status' => TalkStatusEnum::Rejected])) + ->successNotificationTitle('Talk rejected'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventModels/Schemas/EventModelForm.php b/app-modules/panel-admin/src/Filament/Resources/EventModels/Schemas/EventModelForm.php new file mode 100644 index 00000000..f2c2da1e --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventModels/Schemas/EventModelForm.php @@ -0,0 +1,57 @@ +columns(1) + ->components([ + Select::make('tenant_id') + ->required() + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + TextInput::make('title') + ->required() + ->minLength(5) + ->maxLength(255), + TextInput::make('slug') + ->required() + ->unique('events', 'slug', ignoreRecord: true), + RichEditor::make('description') + ->required(), + Select::make('event_type') + ->required() + ->options(EventTypeEnum::class), + TextInput::make('location') + ->required() + ->minLength(5) + ->maxLength(255), + TextInput::make('max_attendees') + ->required() + ->integer() + ->minValue(1), + Toggle::make('active'), + DateTimePicker::make('event_at') + ->required(), + DateTimePicker::make('start_at') + ->required(), + DateTimePicker::make('end_at') + ->required() + ->after('start_at'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventModels/Tables/EventModelsTable.php b/app-modules/panel-admin/src/Filament/Resources/EventModels/Tables/EventModelsTable.php new file mode 100644 index 00000000..3fae44e8 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventModels/Tables/EventModelsTable.php @@ -0,0 +1,61 @@ +columns([ + TextColumn::make('title') + ->searchable() + ->sortable(), + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + TextColumn::make('event_type') + ->badge(), + IconColumn::make('active') + ->boolean(), + TextColumn::make('event_at') + ->date() + ->sortable(), + TextColumn::make('attendees_count') + ->numeric(0) + ->label('Attendees'), + TextColumn::make('waitlist_count') + ->numeric(0) + ->label('Waitlist'), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + SelectFilter::make('event_type') + ->options(EventTypeEnum::class), + TernaryFilter::make('active'), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/EventSubmissionResource.php b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/EventSubmissionResource.php new file mode 100644 index 00000000..66cf79fb --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/EventSubmissionResource.php @@ -0,0 +1,62 @@ + ListEventSubmissions::route('/'), + 'create' => CreateEventSubmission::route('/create'), + 'edit' => EditEventSubmission::route('/{record}/edit'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Pages/CreateEventSubmission.php b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Pages/CreateEventSubmission.php new file mode 100644 index 00000000..1bd36f35 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Pages/CreateEventSubmission.php @@ -0,0 +1,13 @@ +columns([ + TextColumn::make('username') + ->searchable(), + TextColumn::make('name'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Schemas/EventSubmissionForm.php b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Schemas/EventSubmissionForm.php new file mode 100644 index 00000000..ad0befe4 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Schemas/EventSubmissionForm.php @@ -0,0 +1,54 @@ +columns(1) + ->components([ + Select::make('tenant_id') + ->required() + ->relationship('tenant', 'name') + ->searchable() + ->preload() + ->live(), + Select::make('event_id') + ->required() + ->relationship('event', 'title', modifyQueryUsing: fn (Builder $query, Get $get) => $query->where('tenant_id', $get('tenant_id'))) + ->searchable() + ->preload(), + Select::make('user_id') + ->required() + ->relationship('user', 'username') + ->searchable() + ->preload(), + TextInput::make('title') + ->required() + ->minLength(3) + ->maxLength(255), + TextInput::make('field_type') + ->required() + ->minLength(3) + ->maxLength(255), + RichEditor::make('description') + ->required(), + Select::make('status') + ->required() + ->options(TalkStatusEnum::class) + ->default(TalkStatusEnum::Pending), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Tables/EventSubmissionsTable.php b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Tables/EventSubmissionsTable.php new file mode 100644 index 00000000..dea14e27 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/EventSubmissions/Tables/EventSubmissionsTable.php @@ -0,0 +1,53 @@ +columns([ + TextColumn::make('event.title') + ->searchable() + ->label('Event'), + TextColumn::make('user.username') + ->searchable() + ->label('Speaker'), + TextColumn::make('title') + ->searchable(), + TextColumn::make('status') + ->badge(), + TextColumn::make('field_type'), + TextColumn::make('created_at') + ->dateTime() + ->sortable(), + ]) + ->filters([ + SelectFilter::make('status') + ->options(TalkStatusEnum::class), + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/ExternalIdentityResource.php b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/ExternalIdentityResource.php new file mode 100644 index 00000000..cd4f79d6 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/ExternalIdentityResource.php @@ -0,0 +1,83 @@ + + */ + public static function getWidgets(): array + { + return [ + ExternalIdentityStatsOverview::class, + ]; + } + + /** + * @return array + */ + public static function getRelations(): array + { + return [ + MessagesRelationManager::class, + ]; + } + + /** + * @return array + */ + public static function getPages(): array + { + return [ + 'index' => ListExternalIdentities::route('/'), + 'edit' => ViewExternalIdentity::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Pages/ListExternalIdentities.php b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Pages/ListExternalIdentities.php new file mode 100644 index 00000000..9bf312b9 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Pages/ListExternalIdentities.php @@ -0,0 +1,24 @@ +label('Disconnect') + ->icon(Heroicon::NoSymbol) + ->color('danger') + ->visible(fn () => $this->record->isConnected()) + ->requiresConfirmation() + ->action(fn () => $this->record->update(['disconnected_at' => now()])) + ->successNotificationTitle('Identity disconnected'), + DeleteAction::make(), + RestoreAction::make(), + ForceDeleteAction::make(), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/RelationManagers/MessagesRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/RelationManagers/MessagesRelationManager.php new file mode 100644 index 00000000..950e4879 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/RelationManagers/MessagesRelationManager.php @@ -0,0 +1,34 @@ +defaultSort('sent_at', 'desc') + ->columns([ + TextColumn::make('content') + ->limit(60) + ->tooltip(fn ($record) => $record->content), + TextColumn::make('channel_id') + ->label('Channel') + ->copyable(), + TextColumn::make('obtained_experience') + ->label('XP') + ->numeric(0), + TextColumn::make('created_at') + ->dateTime() + ->sortable(), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Schemas/ExternalIdentityForm.php b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Schemas/ExternalIdentityForm.php new file mode 100644 index 00000000..04735504 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Schemas/ExternalIdentityForm.php @@ -0,0 +1,72 @@ +columns(1) + ->components([ + Tabs::make('ExternalIdentity') + ->tabs([ + Tab::make('Identity') + ->icon(Heroicon::Link) + ->schema([ + Placeholder::make('provider') + ->content(fn (?ExternalIdentity $record) => $record?->provider?->getLabel() ?? '-'), + Placeholder::make('type') + ->content(fn (?ExternalIdentity $record) => $record?->type?->getLabel() ?? '-'), + Placeholder::make('credentials_type') + ->label('Credentials Type') + ->content(fn (?ExternalIdentity $record) => $record?->credentials_type?->getLabel() ?? '-'), + Placeholder::make('external_account_id') + ->label('External Account ID') + ->content(fn (?ExternalIdentity $record) => $record?->external_account_id ?? '-'), + Placeholder::make('status') + ->label('Connection Status') + ->content(function (?ExternalIdentity $record): string { + if (! $record instanceof ExternalIdentity) { + return '-'; + } + + if ($record->isConnected()) { + return 'Active (since '.$record->connected_at?->format('Y-m-d H:i').')'; + } + + return 'Disconnected (at '.$record->disconnected_at?->format('Y-m-d H:i').')'; + }), + Placeholder::make('connected_by') + ->label('Connected By') + ->content(fn (?ExternalIdentity $record) => $record?->connectedByUser?->username ?? '-'), + Placeholder::make('created_at') + ->label('Created At') + ->content(fn (?ExternalIdentity $record) => $record?->created_at?->format('Y-m-d H:i') ?? '-'), + ]), + Tab::make('Owner') + ->icon(Heroicon::User) + ->schema([ + Placeholder::make('model_type') + ->label('Owner Type') + ->content(fn (?ExternalIdentity $record) => $record instanceof ExternalIdentity ? class_basename($record->model_type) : '-'), + Placeholder::make('owner_name') + ->label('Name / Username') + ->content(fn (?ExternalIdentity $record) => $record?->user?->username ?? $record?->model?->name ?? '-'), + Placeholder::make('owner_email') + ->label('Email') + ->content(fn (?ExternalIdentity $record) => $record?->user?->email ?? '-'), + ]), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Tables/ExternalIdentitiesTable.php b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Tables/ExternalIdentitiesTable.php new file mode 100644 index 00000000..40ffccc2 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Tables/ExternalIdentitiesTable.php @@ -0,0 +1,94 @@ +columns([ + TextColumn::make('provider') + ->badge() + ->sortable(), + TextColumn::make('user.username') + ->label('Owner') + ->sortable() + ->placeholder('-'), + TextColumn::make('metadata.username') + ->label('Platform User') + ->placeholder('-'), + TextColumn::make('external_account_id') + ->copyable() + ->searchable() + ->label('External ID'), + TextColumn::make('messages_count') + ->label('Messages') + ->numeric() + ->counts('messages') + ->sortable(), + IconColumn::make('status') + ->label('Status') + ->state(fn (ExternalIdentity $record): string => $record->isConnected() ? 'connected' : 'disconnected') + ->icon(fn (string $state): string => match ($state) { + 'connected' => 'heroicon-o-check-circle', + 'disconnected' => 'heroicon-o-x-circle', + }) + ->color(fn (string $state): string => match ($state) { + 'connected' => 'success', + 'disconnected' => 'danger', + }), + TextColumn::make('connected_at') + ->dateTime() + ->sortable(), + TextColumn::make('connectedByUser.username') + ->label('Connected By') + ->placeholder('-'), + ]) + ->filters([ + SelectFilter::make('provider') + ->options(IdentityProvider::class), + SelectFilter::make('model_type') + ->label('Owner Type') + ->options([ + User::class => 'User', + Tenant::class => 'Tenant', + ]), + TernaryFilter::make('connected') + ->label('Connection Status') + ->queries( + true: fn ($query) => $query->whereNotNull('connected_at')->whereNull('disconnected_at'), + false: fn ($query) => $query->whereNotNull('disconnected_at'), + ), + TrashedFilter::make(), + ]) + ->recordActions([ + ViewAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Widgets/ExternalIdentityStatsOverview.php b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Widgets/ExternalIdentityStatsOverview.php new file mode 100644 index 00000000..6fc013d3 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/ExternalIdentities/Widgets/ExternalIdentityStatsOverview.php @@ -0,0 +1,51 @@ +count()) + ->icon(Heroicon::Link), + Stat::make('Active Connections', ExternalIdentity::query() + ->whereNotNull('connected_at') + ->whereNull('disconnected_at') + ->count()) + ->icon(Heroicon::CheckCircle) + ->color('success'), + Stat::make('Disconnected', ExternalIdentity::query() + ->whereNotNull('disconnected_at') + ->count()) + ->icon(Heroicon::XCircle) + ->color('danger'), + Stat::make('Discord', ExternalIdentity::query() + ->where('provider', IdentityProvider::Discord) + ->count()) + ->icon('heroicon-o-chat-bubble-left-right') + ->color('info'), + Stat::make('Twitch', ExternalIdentity::query() + ->where('provider', IdentityProvider::Twitch) + ->count()) + ->icon('heroicon-o-video-camera') + ->color('purple'), + Stat::make('Total Messages', Message::query() + ->whereNotNull('external_identity_id') + ->count()) + ->icon(Heroicon::ChatBubbleLeftEllipsis) + ->color('gray'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Feedback/FeedbackResource.php b/app-modules/panel-admin/src/Filament/Resources/Feedback/FeedbackResource.php new file mode 100644 index 00000000..e006053f --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Feedback/FeedbackResource.php @@ -0,0 +1,69 @@ +whereDoesntHave('review')->count(); + } + + public static function form(Schema $schema): Schema + { + return $schema + ->columns(1) + ->components([ + Placeholder::make('sender') + ->content(fn ($record) => $record?->sender?->username ?? '-'), + Placeholder::make('target') + ->content(fn ($record) => $record?->target?->username ?? '-'), + Placeholder::make('type') + ->content(fn ($record) => $record?->type ?? '-'), + Placeholder::make('message') + ->content(fn ($record) => $record?->message ?? '-'), + Placeholder::make('created_at') + ->content(fn ($record) => $record?->created_at?->format('Y-m-d H:i') ?? '-'), + ]); + } + + public static function table(Table $table): Table + { + return FeedbackTable::configure($table); + } + + public static function getRelations(): array + { + return []; + } + + public static function getPages(): array + { + return [ + 'index' => ListFeedback::route('/'), + 'edit' => EditFeedback::route('/{record}/edit'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Feedback/Pages/EditFeedback.php b/app-modules/panel-admin/src/Filament/Resources/Feedback/Pages/EditFeedback.php new file mode 100644 index 00000000..967c7a99 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Feedback/Pages/EditFeedback.php @@ -0,0 +1,65 @@ +label('Approve') + ->icon(Heroicon::Check) + ->color('success') + ->action(function (): void { + $this->record->review()->updateOrCreate( + ['feedback_id' => $this->record->id], + [ + 'tenant_id' => $this->record->tenant_id, + 'staff_id' => auth()->id(), + 'status' => ReviewTypeEnum::APPROVED, + 'received_at' => now(), + ] + ); + }) + ->successNotificationTitle('Feedback approved'), + + Action::make('decline') + ->label('Decline') + ->icon(Heroicon::XMark) + ->color('danger') + ->form([ + Textarea::make('reason') + ->required() + ->rows(3), + ]) + ->action(function (array $data): void { + $this->record->review()->updateOrCreate( + ['feedback_id' => $this->record->id], + [ + 'tenant_id' => $this->record->tenant_id, + 'staff_id' => auth()->id(), + 'status' => ReviewTypeEnum::DECLINED, + 'reason' => $data['reason'], + 'received_at' => now(), + ] + ); + }) + ->successNotificationTitle('Feedback declined'), + + DeleteAction::make(), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Feedback/Pages/ListFeedback.php b/app-modules/panel-admin/src/Filament/Resources/Feedback/Pages/ListFeedback.php new file mode 100644 index 00000000..3de95790 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Feedback/Pages/ListFeedback.php @@ -0,0 +1,13 @@ +defaultSort('created_at', 'desc') + ->columns([ + TextColumn::make('sender.username') + ->searchable() + ->label('From'), + TextColumn::make('target.username') + ->searchable() + ->label('To'), + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + TextColumn::make('type') + ->badge(), + TextColumn::make('message') + ->limit(60) + ->tooltip(fn ($record) => $record->message), + TextColumn::make('review_status') + ->label('Review') + ->badge() + ->getStateUsing(fn ($record) => $record->review?->status?->value ?? 'pending') + ->color(fn (string $state): string => match ($state) { + 'approved' => 'success', + 'declined' => 'danger', + default => 'warning', + }), + TextColumn::make('created_at') + ->dateTime() + ->sortable(), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + SelectFilter::make('review_status') + ->label('Review Status') + ->options([ + 'pending' => 'Pending', + 'approved' => 'Approved', + 'declined' => 'Declined', + ]) + ->query(function ($query, array $data) { + if (! $data['value']) { + return $query; + } + + return match ($data['value']) { + 'pending' => $query->whereDoesntHave('review'), + default => $query->whereHas('review', fn ($q) => $q->where('status', $data['value'])), + }; + }), + ]) + ->recordActions([ + EditAction::make(), + ]); + } +} diff --git a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/MeetingTypeResource.php b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/MeetingTypeResource.php similarity index 66% rename from app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/MeetingTypeResource.php rename to app-modules/panel-admin/src/Filament/Resources/MeetingTypes/MeetingTypeResource.php index d6420b6a..34eb2be3 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/MeetingTypeResource.php +++ b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/MeetingTypeResource.php @@ -2,29 +2,31 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\MeetingTypes; +namespace He4rt\PanelAdmin\Filament\Resources\MeetingTypes; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Pages\CreateMeetingType; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Pages\EditMeetingType; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Pages\ListMeetingTypes; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Schemas\MeetingTypeForm; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Tables\MeetingTypesTable; use He4rt\Community\Meeting\Models\MeetingType; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Pages\CreateMeetingType; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Pages\EditMeetingType; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Pages\ListMeetingTypes; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Schemas\MeetingTypeForm; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Tables\MeetingTypesTable; use UnitEnum; class MeetingTypeResource extends Resource { protected static ?string $model = MeetingType::class; - protected static string|BackedEnum|null $navigationIcon = Heroicon::ChatBubbleBottomCenterText; + protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedClipboardDocumentList; protected static string|UnitEnum|null $navigationGroup = 'Meetings'; + protected static ?int $navigationSort = 2; + protected static ?string $recordTitleAttribute = 'name'; public static function form(Schema $schema): Schema @@ -39,9 +41,7 @@ public static function table(Table $table): Table public static function getRelations(): array { - return [ - - ]; + return []; } public static function getPages(): array diff --git a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/CreateMeetingType.php b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/CreateMeetingType.php similarity index 55% rename from app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/CreateMeetingType.php rename to app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/CreateMeetingType.php index e9051525..85eba114 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/CreateMeetingType.php +++ b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/CreateMeetingType.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\MeetingTypeResource; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\MeetingTypeResource; class CreateMeetingType extends CreateRecord { diff --git a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/EditMeetingType.php b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/EditMeetingType.php similarity index 69% rename from app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/EditMeetingType.php rename to app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/EditMeetingType.php index 38fcc034..2636e43a 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/EditMeetingType.php +++ b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/EditMeetingType.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Pages; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\MeetingTypeResource; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\MeetingTypeResource; class EditMeetingType extends EditRecord { diff --git a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/ListMeetingTypes.php b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/ListMeetingTypes.php similarity index 69% rename from app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/ListMeetingTypes.php rename to app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/ListMeetingTypes.php index 59c855ee..80ffd4a0 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Pages/ListMeetingTypes.php +++ b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Pages/ListMeetingTypes.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Community\Meeting\Filament\Resources\MeetingTypes\MeetingTypeResource; +use He4rt\PanelAdmin\Filament\Resources\MeetingTypes\MeetingTypeResource; class ListMeetingTypes extends ListRecords { diff --git a/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Schemas/MeetingTypeForm.php b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Schemas/MeetingTypeForm.php new file mode 100644 index 00000000..544957e2 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Schemas/MeetingTypeForm.php @@ -0,0 +1,38 @@ +columns(1) + ->components([ + TextInput::make('name') + ->required() + ->maxLength(255), + Select::make('week_day') + ->required() + ->options([ + 0 => 'Domingo', + 1 => 'Segunda', + 2 => 'Terça', + 3 => 'Quarta', + 4 => 'Quinta', + 5 => 'Sexta', + 6 => 'Sábado', + ]), + TextInput::make('start_at') + ->required() + ->integer() + ->helperText('Minutes from midnight (e.g., 1200 = 20:00)'), + ]); + } +} diff --git a/app-modules/events/src/Filament/Resources/Sponsors/Tables/SponsorsTable.php b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Tables/MeetingTypesTable.php similarity index 62% rename from app-modules/events/src/Filament/Resources/Sponsors/Tables/SponsorsTable.php rename to app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Tables/MeetingTypesTable.php index 8c53b71d..1fd8098c 100644 --- a/app-modules/events/src/Filament/Resources/Sponsors/Tables/SponsorsTable.php +++ b/app-modules/panel-admin/src/Filament/Resources/MeetingTypes/Tables/MeetingTypesTable.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Resources\Sponsors\Tables; +namespace He4rt\PanelAdmin\Filament\Resources\MeetingTypes\Tables; use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; @@ -10,20 +10,21 @@ use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; -class SponsorsTable +class MeetingTypesTable { public static function configure(Table $table): Table { return $table ->columns([ TextColumn::make('name') - ->searchable(), - TextColumn::make('homepage_url') - ->searchable(), - ]) - ->filters([ - + ->searchable() + ->sortable(), + TextColumn::make('meeting_day_for_humans') + ->label('Day'), + TextColumn::make('start_at') + ->label('Start Time'), ]) + ->filters([]) ->recordActions([ EditAction::make(), ]) diff --git a/app-modules/community/src/Meeting/Filament/Resources/Meetings/MeetingResource.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/MeetingResource.php similarity index 62% rename from app-modules/community/src/Meeting/Filament/Resources/Meetings/MeetingResource.php rename to app-modules/panel-admin/src/Filament/Resources/Meetings/MeetingResource.php index 1faf72e9..a4b99670 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/Meetings/MeetingResource.php +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/MeetingResource.php @@ -2,30 +2,32 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\Meetings; +namespace He4rt\PanelAdmin\Filament\Resources\Meetings; -use App\Filament\Shared\RelationManagers\MembersRelationManager; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; -use He4rt\Community\Meeting\Filament\Resources\Meetings\Pages\CreateMeeting; -use He4rt\Community\Meeting\Filament\Resources\Meetings\Pages\EditMeeting; -use He4rt\Community\Meeting\Filament\Resources\Meetings\Pages\ListMeetings; -use He4rt\Community\Meeting\Filament\Resources\Meetings\Schemas\MeetingForm; -use He4rt\Community\Meeting\Filament\Resources\Meetings\Tables\MeetingsTable; use He4rt\Community\Meeting\Models\Meeting; +use He4rt\PanelAdmin\Filament\Resources\Meetings\Pages\CreateMeeting; +use He4rt\PanelAdmin\Filament\Resources\Meetings\Pages\EditMeeting; +use He4rt\PanelAdmin\Filament\Resources\Meetings\Pages\ListMeetings; +use He4rt\PanelAdmin\Filament\Resources\Meetings\RelationManagers\ParticipantsRelationManager; +use He4rt\PanelAdmin\Filament\Resources\Meetings\Schemas\MeetingForm; +use He4rt\PanelAdmin\Filament\Resources\Meetings\Tables\MeetingsTable; use UnitEnum; class MeetingResource extends Resource { protected static ?string $model = Meeting::class; - protected static string|BackedEnum|null $navigationIcon = Heroicon::UserGroup; + protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedVideoCamera; protected static string|UnitEnum|null $navigationGroup = 'Meetings'; + protected static ?int $navigationSort = 1; + public static function form(Schema $schema): Schema { return MeetingForm::configure($schema); @@ -39,7 +41,7 @@ public static function table(Table $table): Table public static function getRelations(): array { return [ - MembersRelationManager::class, + ParticipantsRelationManager::class, ]; } diff --git a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/CreateMeeting.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/CreateMeeting.php similarity index 56% rename from app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/CreateMeeting.php rename to app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/CreateMeeting.php index c861ce37..a38731b3 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/CreateMeeting.php +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/CreateMeeting.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\Meetings\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Meetings\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Community\Meeting\Filament\Resources\Meetings\MeetingResource; +use He4rt\PanelAdmin\Filament\Resources\Meetings\MeetingResource; class CreateMeeting extends CreateRecord { diff --git a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/EditMeeting.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/EditMeeting.php similarity index 70% rename from app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/EditMeeting.php rename to app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/EditMeeting.php index 599a6afc..21aed661 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/EditMeeting.php +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/EditMeeting.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\Meetings\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Meetings\Pages; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Community\Meeting\Filament\Resources\Meetings\MeetingResource; +use He4rt\PanelAdmin\Filament\Resources\Meetings\MeetingResource; class EditMeeting extends EditRecord { diff --git a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/ListMeetings.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/ListMeetings.php similarity index 70% rename from app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/ListMeetings.php rename to app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/ListMeetings.php index e391234b..31fb5eaf 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Pages/ListMeetings.php +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/Pages/ListMeetings.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\Meetings\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Meetings\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Community\Meeting\Filament\Resources\Meetings\MeetingResource; +use He4rt\PanelAdmin\Filament\Resources\Meetings\MeetingResource; class ListMeetings extends ListRecords { diff --git a/app-modules/panel-admin/src/Filament/Resources/Meetings/RelationManagers/ParticipantsRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/RelationManagers/ParticipantsRelationManager.php new file mode 100644 index 00000000..d603104b --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/RelationManagers/ParticipantsRelationManager.php @@ -0,0 +1,27 @@ +columns([ + TextColumn::make('username') + ->searchable(), + TextColumn::make('name'), + TextColumn::make('attend_at') + ->dateTime() + ->label('Attended At'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Meetings/Schemas/MeetingForm.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/Schemas/MeetingForm.php new file mode 100644 index 00000000..1254e871 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/Schemas/MeetingForm.php @@ -0,0 +1,42 @@ +columns(1) + ->components([ + Select::make('tenant_id') + ->required() + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + Select::make('meeting_type_id') + ->required() + ->relationship('meetingType', 'name') + ->preload(), + Select::make('admin_id') + ->required() + ->relationship('admin', 'username') + ->searchable() + ->preload() + ->default(fn () => auth()->id()), + RichEditor::make('content') + ->nullable(), + DateTimePicker::make('starts_at') + ->required(), + DateTimePicker::make('ends_at') + ->nullable(), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Meetings/Tables/MeetingsTable.php b/app-modules/panel-admin/src/Filament/Resources/Meetings/Tables/MeetingsTable.php new file mode 100644 index 00000000..0021d7b5 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Meetings/Tables/MeetingsTable.php @@ -0,0 +1,64 @@ +columns([ + TextColumn::make('meetingType.name') + ->badge() + ->color('primary') + ->label('Type'), + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + TextColumn::make('admin.username') + ->label('Host'), + TextColumn::make('starts_at') + ->dateTime() + ->sortable(), + TextColumn::make('ends_at') + ->dateTime() + ->placeholder('Ongoing'), + TextColumn::make('members_count') + ->counts('members') + ->label('Participants'), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + ]) + ->recordActions([ + Action::make('endMeeting') + ->label('End Meeting') + ->icon(Heroicon::Stop) + ->color('warning') + ->visible(fn ($record) => $record->ends_at === null) + ->requiresConfirmation() + ->action(fn ($record) => $record->update(['ends_at' => now()])) + ->successNotificationTitle('Meeting ended'), + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Messages/MessageResource.php b/app-modules/panel-admin/src/Filament/Resources/Messages/MessageResource.php new file mode 100644 index 00000000..7bbd8b0e --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Messages/MessageResource.php @@ -0,0 +1,48 @@ +components([]); + } + + public static function table(Table $table): Table + { + return MessagesTable::configure($table); + } + + public static function getRelations(): array + { + return []; + } + + public static function getPages(): array + { + return [ + 'index' => ListMessages::route('/'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Messages/Pages/ListMessages.php b/app-modules/panel-admin/src/Filament/Resources/Messages/Pages/ListMessages.php new file mode 100644 index 00000000..48913747 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Messages/Pages/ListMessages.php @@ -0,0 +1,13 @@ +defaultSort('sent_at', 'desc') + ->columns([ + TextColumn::make('provider.user.username') + ->label('User') + ->searchable(), + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + TextColumn::make('channel_id'), + TextColumn::make('content') + ->limit(80) + ->tooltip(fn ($record) => $record->content) + ->searchable(), + TextColumn::make('obtained_experience') + ->numeric(0) + ->label('XP'), + TextColumn::make('sent_at') + ->dateTime() + ->sortable(), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + ]) + ->recordActions([ + DeleteAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/CreateSeason.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/CreateSeason.php similarity index 54% rename from app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/CreateSeason.php rename to app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/CreateSeason.php index 7ea63aec..cccf34fd 100644 --- a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/CreateSeason.php +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/CreateSeason.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Seasons\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\SeasonResource; +use He4rt\PanelAdmin\Filament\Resources\Seasons\SeasonResource; class CreateSeason extends CreateRecord { diff --git a/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/EditSeason.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/EditSeason.php new file mode 100644 index 00000000..1071da79 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/EditSeason.php @@ -0,0 +1,64 @@ +label('End Season') + ->icon(Heroicon::Stop) + ->color('danger') + ->visible(fn () => $this->record->ended_at === null) + ->requiresConfirmation() + ->modalDescription('This will set ended_at to now.') + ->action(fn () => $this->record->update(['ended_at' => now()])) + ->successNotificationTitle('Season ended'), + + Action::make('computeRankings') + ->label('Compute Rankings') + ->icon(Heroicon::ChartBar) + ->color('warning') + ->requiresConfirmation() + ->modalDescription('This will snapshot all character rankings for this season.') + ->action(function (): void { + $characters = Character::query() + ->where('tenant_id', $this->record->tenant_id) + ->orderByDesc('experience') + ->get(); + + $position = 1; + foreach ($characters as $character) { + PastSeason::query()->create([ + 'tenant_id' => $this->record->tenant_id, + 'season_id' => $this->record->id, + 'character_id' => $character->id, + 'ranking_position' => $position++, + 'level' => $character->level, + 'experience' => $character->experience, + 'messages_count' => 0, + 'badges_count' => $character->badges()->count(), + 'meetings_count' => 0, + ]); + } + }) + ->successNotificationTitle('Rankings computed'), + + DeleteAction::make(), + ]; + } +} diff --git a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/ListSeasons.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/ListSeasons.php similarity index 68% rename from app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/ListSeasons.php rename to app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/ListSeasons.php index e81b0260..7c574433 100644 --- a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/Pages/ListSeasons.php +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/Pages/ListSeasons.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Seasons\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\SeasonResource; +use He4rt\PanelAdmin\Filament\Resources\Seasons\SeasonResource; class ListSeasons extends ListRecords { diff --git a/app-modules/panel-admin/src/Filament/Resources/Seasons/RelationManagers/RankingsRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/RelationManagers/RankingsRelationManager.php new file mode 100644 index 00000000..2d3244b4 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/RelationManagers/RankingsRelationManager.php @@ -0,0 +1,38 @@ +defaultSort('ranking_position') + ->columns([ + TextColumn::make('ranking_position') + ->sortable() + ->label('#'), + + TextColumn::make('character.user.username') + ->label('User'), + + TextColumn::make('experience') + ->numeric(0) + ->sortable(), + + TextColumn::make('messages_count') + ->numeric(0), + + TextColumn::make('badges_count') + ->numeric(0), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Seasons/Schemas/SeasonForm.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/Schemas/SeasonForm.php new file mode 100644 index 00000000..8fee6a68 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/Schemas/SeasonForm.php @@ -0,0 +1,42 @@ +columns(1) + ->components([ + Select::make('tenant_id') + ->required() + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + + TextInput::make('name') + ->required() + ->maxLength(255), + + Textarea::make('description') + ->nullable() + ->rows(3), + + DateTimePicker::make('started_at') + ->required(), + + DateTimePicker::make('ended_at') + ->nullable() + ->after('started_at'), + ]); + } +} diff --git a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/SeasonResource.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/SeasonResource.php similarity index 55% rename from app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/SeasonResource.php rename to app-modules/panel-admin/src/Filament/Resources/Seasons/SeasonResource.php index 7cf025a0..387af7da 100644 --- a/app-modules/gamification/src/Season/Filament/Admin/Resources/Seasons/SeasonResource.php +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/SeasonResource.php @@ -2,28 +2,31 @@ declare(strict_types=1); -namespace He4rt\Gamification\Season\Filament\Admin\Resources\Seasons; +namespace He4rt\PanelAdmin\Filament\Resources\Seasons; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Pages\CreateSeason; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Pages\EditSeason; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Pages\ListSeasons; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Schemas\SeasonForm; -use He4rt\Gamification\Season\Filament\Admin\Resources\Seasons\Tables\SeasonsTable; use He4rt\Gamification\Season\Models\Season; +use He4rt\PanelAdmin\Filament\Resources\Seasons\Pages\CreateSeason; +use He4rt\PanelAdmin\Filament\Resources\Seasons\Pages\EditSeason; +use He4rt\PanelAdmin\Filament\Resources\Seasons\Pages\ListSeasons; +use He4rt\PanelAdmin\Filament\Resources\Seasons\RelationManagers\RankingsRelationManager; +use He4rt\PanelAdmin\Filament\Resources\Seasons\Schemas\SeasonForm; +use He4rt\PanelAdmin\Filament\Resources\Seasons\Tables\SeasonsTable; use UnitEnum; class SeasonResource extends Resource { protected static ?string $model = Season::class; - protected static string|UnitEnum|null $navigationGroup = 'Gamefication'; + protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedFlag; - protected static string|BackedEnum|null $navigationIcon = Heroicon::RectangleGroup; + protected static string|null|UnitEnum $navigationGroup = 'Gamification'; + + protected static ?int $navigationSort = 3; protected static ?string $recordTitleAttribute = 'name'; @@ -37,6 +40,13 @@ public static function table(Table $table): Table return SeasonsTable::configure($table); } + public static function getRelations(): array + { + return [ + RankingsRelationManager::class, + ]; + } + public static function getPages(): array { return [ diff --git a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Tables/MeetingsTable.php b/app-modules/panel-admin/src/Filament/Resources/Seasons/Tables/SeasonsTable.php similarity index 52% rename from app-modules/community/src/Meeting/Filament/Resources/Meetings/Tables/MeetingsTable.php rename to app-modules/panel-admin/src/Filament/Resources/Seasons/Tables/SeasonsTable.php index 007e37bc..c4838c11 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/Meetings/Tables/MeetingsTable.php +++ b/app-modules/panel-admin/src/Filament/Resources/Seasons/Tables/SeasonsTable.php @@ -2,43 +2,49 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\Meetings\Tables; +namespace He4rt\PanelAdmin\Filament\Resources\Seasons\Tables; use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Table; -class MeetingsTable +class SeasonsTable { public static function configure(Table $table): Table { return $table ->columns([ - TextColumn::make('tenant.name') - ->badge() - ->searchable() - ->sortable(), - TextColumn::make('admin.name') - ->label('Admin') + TextColumn::make('name') ->searchable() - ->badge(), - TextColumn::make('meetingType.name') ->sortable(), - TextColumn::make('starts_at') - ->label('Meeting Hour') - ->formatStateUsing(fn ($state) => $state->format('d/m/Y H:i')) - ->description(fn ($record) => $record->ends_at->format('d/m/Y H:i')) - ->sortable(), - TextColumn::make('created_at') + + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + + TextColumn::make('started_at') ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('updated_at') + ->sortable(), + + TextColumn::make('ended_at') ->dateTime() ->sortable() - ->toggleable(isToggledHiddenByDefault: true), + ->placeholder('Active'), + + TextColumn::make('participants_count') + ->numeric(0), + + TextColumn::make('messages_count') + ->numeric(0), + ]) + ->filters([ + SelectFilter::make('tenant_id') + ->relationship('tenant', 'name') + ->searchable() + ->preload(), ]) ->recordActions([ EditAction::make(), diff --git a/app-modules/events/src/Filament/Resources/Sponsors/Pages/CreateSponsor.php b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/CreateSponsor.php similarity index 59% rename from app-modules/events/src/Filament/Resources/Sponsors/Pages/CreateSponsor.php rename to app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/CreateSponsor.php index 9490e1cc..0391a239 100644 --- a/app-modules/events/src/Filament/Resources/Sponsors/Pages/CreateSponsor.php +++ b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/CreateSponsor.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Resources\Sponsors\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Sponsors\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Events\Filament\Resources\Sponsors\SponsorResource; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\SponsorResource; class CreateSponsor extends CreateRecord { diff --git a/app-modules/events/src/Filament/Resources/Sponsors/Pages/EditSponsor.php b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/EditSponsor.php similarity index 72% rename from app-modules/events/src/Filament/Resources/Sponsors/Pages/EditSponsor.php rename to app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/EditSponsor.php index b64bb757..a5c011e6 100644 --- a/app-modules/events/src/Filament/Resources/Sponsors/Pages/EditSponsor.php +++ b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/EditSponsor.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Resources\Sponsors\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Sponsors\Pages; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Events\Filament\Resources\Sponsors\SponsorResource; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\SponsorResource; class EditSponsor extends EditRecord { diff --git a/app-modules/events/src/Filament/Resources/Sponsors/Pages/ListSponsors.php b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/ListSponsors.php similarity index 72% rename from app-modules/events/src/Filament/Resources/Sponsors/Pages/ListSponsors.php rename to app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/ListSponsors.php index e1fc2ec0..e2abaf2d 100644 --- a/app-modules/events/src/Filament/Resources/Sponsors/Pages/ListSponsors.php +++ b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Pages/ListSponsors.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Resources\Sponsors\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Sponsors\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Events\Filament\Resources\Sponsors\SponsorResource; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\SponsorResource; class ListSponsors extends ListRecords { diff --git a/app-modules/panel-admin/src/Filament/Resources/Sponsors/Schemas/SponsorForm.php b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Schemas/SponsorForm.php new file mode 100644 index 00000000..75dee46b --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Schemas/SponsorForm.php @@ -0,0 +1,35 @@ +columns(1) + ->components([ + Select::make('tenant_id') + ->required() + ->relationship('tenant', 'name') + ->searchable() + ->preload(), + TextInput::make('name') + ->required() + ->maxLength(255), + TextInput::make('homepage_url') + ->required() + ->url(), + SpatieMediaLibraryFileUpload::make('receipt_image') + ->collection('receipt') + ->disk('public'), + ]); + } +} diff --git a/app-modules/events/src/Filament/Resources/Sponsors/SponsorResource.php b/app-modules/panel-admin/src/Filament/Resources/Sponsors/SponsorResource.php similarity index 62% rename from app-modules/events/src/Filament/Resources/Sponsors/SponsorResource.php rename to app-modules/panel-admin/src/Filament/Resources/Sponsors/SponsorResource.php index 5f64b2d1..f3243982 100644 --- a/app-modules/events/src/Filament/Resources/Sponsors/SponsorResource.php +++ b/app-modules/panel-admin/src/Filament/Resources/Sponsors/SponsorResource.php @@ -2,29 +2,30 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Resources\Sponsors; +namespace He4rt\PanelAdmin\Filament\Resources\Sponsors; -use App\Filament\Shared\RelationManagers\EventsRelationManager; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; -use He4rt\Events\Filament\Resources\Sponsors\Pages\CreateSponsor; -use He4rt\Events\Filament\Resources\Sponsors\Pages\EditSponsor; -use He4rt\Events\Filament\Resources\Sponsors\Pages\ListSponsors; -use He4rt\Events\Filament\Resources\Sponsors\Schemas\SponsorForm; -use He4rt\Events\Filament\Resources\Sponsors\Tables\SponsorsTable; use He4rt\Events\Models\Sponsor; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\Pages\CreateSponsor; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\Pages\EditSponsor; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\Pages\ListSponsors; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\Schemas\SponsorForm; +use He4rt\PanelAdmin\Filament\Resources\Sponsors\Tables\SponsorsTable; use UnitEnum; class SponsorResource extends Resource { protected static ?string $model = Sponsor::class; - protected static string|UnitEnum|null $navigationGroup = 'General'; + protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedGift; - protected static string|BackedEnum|null $navigationIcon = Heroicon::Banknotes; + protected static string|UnitEnum|null $navigationGroup = 'Events'; + + protected static ?int $navigationSort = 3; protected static ?string $recordTitleAttribute = 'name'; @@ -40,9 +41,7 @@ public static function table(Table $table): Table public static function getRelations(): array { - return [ - EventsRelationManager::class, - ]; + return []; } public static function getPages(): array diff --git a/app-modules/identity/src/Filament/Admin/Resources/Users/Tables/UsersTable.php b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Tables/SponsorsTable.php similarity index 57% rename from app-modules/identity/src/Filament/Admin/Resources/Users/Tables/UsersTable.php rename to app-modules/panel-admin/src/Filament/Resources/Sponsors/Tables/SponsorsTable.php index 314dbe07..7654edad 100644 --- a/app-modules/identity/src/Filament/Admin/Resources/Users/Tables/UsersTable.php +++ b/app-modules/panel-admin/src/Filament/Resources/Sponsors/Tables/SponsorsTable.php @@ -2,33 +2,34 @@ declare(strict_types=1); -namespace He4rt\Identity\Filament\Admin\Resources\Users\Tables; +namespace He4rt\PanelAdmin\Filament\Resources\Sponsors\Tables; use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; -use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; -class UsersTable +class SponsorsTable { public static function configure(Table $table): Table { return $table ->columns([ - TextColumn::make('username') - ->searchable() - ->sortable(), TextColumn::make('name') ->searchable() ->sortable(), - TextColumn::make('email') - ->searchable() - ->sortable(), - IconColumn::make('is_donator') - ->sortable(), + TextColumn::make('tenant.name') + ->badge() + ->color('gray'), + TextColumn::make('homepage_url') + ->limit(30) + ->url(fn ($record) => $record->homepage_url, true), + TextColumn::make('events_count') + ->counts('events') + ->label('Events'), ]) + ->filters([]) ->recordActions([ EditAction::make(), ]) diff --git a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/CreateTenant.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/CreateTenant.php similarity index 58% rename from app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/CreateTenant.php rename to app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/CreateTenant.php index 5bd5bc5f..8d0e9366 100644 --- a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/CreateTenant.php +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/CreateTenant.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Identity\Filament\Admin\Resources\Tenants\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Tenants\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Identity\Filament\Admin\Resources\Tenants\TenantResource; +use He4rt\PanelAdmin\Filament\Resources\Tenants\TenantResource; class CreateTenant extends CreateRecord { diff --git a/app-modules/events/src/Filament/Admin/Resources/EventAgenda/Pages/EditEventAgenda.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/EditTenant.php similarity index 60% rename from app-modules/events/src/Filament/Admin/Resources/EventAgenda/Pages/EditEventAgenda.php rename to app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/EditTenant.php index af2e8ec4..e4fc41bd 100644 --- a/app-modules/events/src/Filament/Admin/Resources/EventAgenda/Pages/EditEventAgenda.php +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/EditTenant.php @@ -2,17 +2,17 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Admin\Resources\EventAgenda\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Tenants\Pages; use Filament\Actions\DeleteAction; use Filament\Actions\ForceDeleteAction; use Filament\Actions\RestoreAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Events\Filament\Admin\Resources\EventAgenda\EventAgendaResource; +use He4rt\PanelAdmin\Filament\Resources\Tenants\TenantResource; -class EditEventAgenda extends EditRecord +class EditTenant extends EditRecord { - protected static string $resource = EventAgendaResource::class; + protected static string $resource = TenantResource::class; protected function getHeaderActions(): array { diff --git a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/ListTenants.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/ListTenants.php similarity index 71% rename from app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/ListTenants.php rename to app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/ListTenants.php index c11de335..e7472f80 100644 --- a/app-modules/identity/src/Filament/Admin/Resources/Tenants/Pages/ListTenants.php +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/Pages/ListTenants.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace He4rt\Identity\Filament\Admin\Resources\Tenants\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Tenants\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Identity\Filament\Admin\Resources\Tenants\TenantResource; +use He4rt\PanelAdmin\Filament\Resources\Tenants\TenantResource; class ListTenants extends ListRecords { diff --git a/app-modules/panel-admin/src/Filament/Resources/Tenants/RelationManagers/MembersRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/RelationManagers/MembersRelationManager.php new file mode 100644 index 00000000..fb7755e7 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/RelationManagers/MembersRelationManager.php @@ -0,0 +1,26 @@ +columns([ + TextColumn::make('username') + ->searchable(), + TextColumn::make('name') + ->searchable(), + TextColumn::make('email'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Tenants/Schemas/TenantForm.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/Schemas/TenantForm.php new file mode 100644 index 00000000..366e0454 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/Schemas/TenantForm.php @@ -0,0 +1,38 @@ +columns(1) + ->components([ + TextInput::make('name') + ->required() + ->maxLength(255), + TextInput::make('slug') + ->required() + ->maxLength(100) + ->unique('tenants', 'slug', ignoreRecord: true), + TextInput::make('domain') + ->nullable() + ->maxLength(255), + Select::make('owner_id') + ->relationship('owner', 'username') + ->searchable() + ->preload() + ->required(), + Toggle::make('active') + ->default(true), + ]); + } +} diff --git a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Tables/BadgesTable.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/Tables/TenantsTable.php similarity index 53% rename from app-modules/gamification/src/Badge/Filament/Resources/Badges/Tables/BadgesTable.php rename to app-modules/panel-admin/src/Filament/Resources/Tenants/Tables/TenantsTable.php index 1a24d879..11b7667a 100644 --- a/app-modules/gamification/src/Badge/Filament/Resources/Badges/Tables/BadgesTable.php +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/Tables/TenantsTable.php @@ -2,43 +2,47 @@ declare(strict_types=1); -namespace He4rt\Gamification\Badge\Filament\Resources\Badges\Tables; +namespace He4rt\PanelAdmin\Filament\Resources\Tenants\Tables; use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; +use Filament\Actions\ForceDeleteBulkAction; +use Filament\Actions\RestoreBulkAction; use Filament\Tables\Columns\IconColumn; -use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\TernaryFilter; +use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; -class BadgesTable +class TenantsTable { public static function configure(Table $table): Table { return $table ->columns([ - TextColumn::make('provider') - ->badge() - ->searchable(), TextColumn::make('name') - ->searchable(), - ImageColumn::make('image_url'), - TextColumn::make('redeem_code') - ->searchable(), + ->searchable() + ->sortable(), + TextColumn::make('slug') + ->badge() + ->color('gray'), + TextColumn::make('owner.username') + ->searchable() + ->label('Owner'), IconColumn::make('active') ->boolean(), + TextColumn::make('members_count') + ->counts('members') + ->label('Members'), TextColumn::make('created_at') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('updated_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('tenant_id') - ->numeric() - ->sortable(), + ]) + ->filters([ + TernaryFilter::make('active'), + TrashedFilter::make(), ]) ->recordActions([ EditAction::make(), @@ -46,6 +50,8 @@ public static function configure(Table $table): Table ->toolbarActions([ BulkActionGroup::make([ DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), ]), ]); } diff --git a/app-modules/identity/src/Filament/Admin/Resources/Tenants/TenantResource.php b/app-modules/panel-admin/src/Filament/Resources/Tenants/TenantResource.php similarity index 52% rename from app-modules/identity/src/Filament/Admin/Resources/Tenants/TenantResource.php rename to app-modules/panel-admin/src/Filament/Resources/Tenants/TenantResource.php index 83759b9f..ab222715 100644 --- a/app-modules/identity/src/Filament/Admin/Resources/Tenants/TenantResource.php +++ b/app-modules/panel-admin/src/Filament/Resources/Tenants/TenantResource.php @@ -2,40 +2,36 @@ declare(strict_types=1); -namespace He4rt\Identity\Filament\Admin\Resources\Tenants; +namespace He4rt\PanelAdmin\Filament\Resources\Tenants; -use App\Filament\Shared\RelationManagers\EventsRelationManager; -use App\Filament\Shared\RelationManagers\MembersRelationManager; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; -use He4rt\Identity\Filament\Admin\Resources\Tenants\Pages\CreateTenant; -use He4rt\Identity\Filament\Admin\Resources\Tenants\Pages\EditTenant; -use He4rt\Identity\Filament\Admin\Resources\Tenants\Pages\ListTenants; -use He4rt\Identity\Filament\Admin\Resources\Tenants\Schemas\TenantForm; -use He4rt\Identity\Filament\Admin\Resources\Tenants\Tables\TenantsTable; use He4rt\Identity\Tenant\Models\Tenant; +use He4rt\PanelAdmin\Filament\Resources\Tenants\Pages\CreateTenant; +use He4rt\PanelAdmin\Filament\Resources\Tenants\Pages\EditTenant; +use He4rt\PanelAdmin\Filament\Resources\Tenants\Pages\ListTenants; +use He4rt\PanelAdmin\Filament\Resources\Tenants\RelationManagers\MembersRelationManager; +use He4rt\PanelAdmin\Filament\Resources\Tenants\Schemas\TenantForm; +use He4rt\PanelAdmin\Filament\Resources\Tenants\Tables\TenantsTable; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\SoftDeletingScope; use UnitEnum; class TenantResource extends Resource { protected static ?string $model = Tenant::class; - protected static string|UnitEnum|null $navigationGroup = 'Administration'; + protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedBuildingOffice; - protected static ?int $navigationSort = 2; + protected static string|UnitEnum|null $navigationGroup = 'Community'; - protected static string|BackedEnum|null $navigationIcon = Heroicon::Cube; + protected static ?int $navigationSort = 2; protected static ?string $recordTitleAttribute = 'name'; - public static function getNavigationBadge(): ?string - { - return (string) self::$model::count(); - } - public static function form(Schema $schema): Schema { return TenantForm::configure($schema); @@ -46,14 +42,19 @@ public static function table(Table $table): Table return TenantsTable::configure($table); } + /** + * @return array + */ public static function getRelations(): array { return [ - EventsRelationManager::class, MembersRelationManager::class, ]; } + /** + * @return array + */ public static function getPages(): array { return [ @@ -62,4 +63,12 @@ public static function getPages(): array 'edit' => EditTenant::route('/{record}/edit'), ]; } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } } diff --git a/app-modules/panel-admin/src/Filament/Resources/Transactions/Pages/ListTransactions.php b/app-modules/panel-admin/src/Filament/Resources/Transactions/Pages/ListTransactions.php new file mode 100644 index 00000000..6fdbbc42 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Transactions/Pages/ListTransactions.php @@ -0,0 +1,13 @@ +defaultSort('created_at', 'desc') + ->columns([ + TextColumn::make('type') + ->badge(), + TextColumn::make('amount') + ->numeric(0) + ->color(fn (int $state): string => $state >= 0 ? 'success' : 'danger'), + TextColumn::make('balance_after') + ->numeric(0), + TextColumn::make('description') + ->limit(50), + TextColumn::make('reference_type') + ->badge() + ->color('gray') + ->formatStateUsing(fn (?string $state) => $state ? class_basename($state) : '-'), + TextColumn::make('created_at') + ->dateTime() + ->sortable(), + ]) + ->filters([ + SelectFilter::make('type') + ->options(TransactionType::class), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Transactions/TransactionResource.php b/app-modules/panel-admin/src/Filament/Resources/Transactions/TransactionResource.php new file mode 100644 index 00000000..9712c77c --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Transactions/TransactionResource.php @@ -0,0 +1,48 @@ +components([]); + } + + public static function table(Table $table): Table + { + return TransactionsTable::configure($table); + } + + public static function getRelations(): array + { + return []; + } + + public static function getPages(): array + { + return [ + 'index' => ListTransactions::route('/'), + ]; + } +} diff --git a/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/CreateUser.php b/app-modules/panel-admin/src/Filament/Resources/Users/Pages/CreateUser.php similarity index 58% rename from app-modules/identity/src/Filament/Admin/Resources/Users/Pages/CreateUser.php rename to app-modules/panel-admin/src/Filament/Resources/Users/Pages/CreateUser.php index d1bd92c5..d28901e7 100644 --- a/app-modules/identity/src/Filament/Admin/Resources/Users/Pages/CreateUser.php +++ b/app-modules/panel-admin/src/Filament/Resources/Users/Pages/CreateUser.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace He4rt\Identity\Filament\Admin\Resources\Users\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Users\Pages; use Filament\Resources\Pages\CreateRecord; -use He4rt\Identity\Filament\Admin\Resources\Users\UserResource; +use He4rt\PanelAdmin\Filament\Resources\Users\UserResource; class CreateUser extends CreateRecord { diff --git a/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/EditTalk.php b/app-modules/panel-admin/src/Filament/Resources/Users/Pages/EditUser.php similarity index 52% rename from app-modules/events/src/Filament/Admin/Resources/Talks/Pages/EditTalk.php rename to app-modules/panel-admin/src/Filament/Resources/Users/Pages/EditUser.php index d1300100..621266d5 100644 --- a/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/EditTalk.php +++ b/app-modules/panel-admin/src/Filament/Resources/Users/Pages/EditUser.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Admin\Resources\Talks\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Users\Pages; use Filament\Actions\DeleteAction; use Filament\Resources\Pages\EditRecord; -use He4rt\Events\Filament\Admin\Resources\Talks\TalkResource; +use He4rt\PanelAdmin\Filament\Resources\Users\UserResource; -class EditTalk extends EditRecord +class EditUser extends EditRecord { - protected static string $resource = TalkResource::class; + protected static string $resource = UserResource::class; protected function getHeaderActions(): array { diff --git a/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/ListTalks.php b/app-modules/panel-admin/src/Filament/Resources/Users/Pages/ListUsers.php similarity index 52% rename from app-modules/events/src/Filament/Admin/Resources/Talks/Pages/ListTalks.php rename to app-modules/panel-admin/src/Filament/Resources/Users/Pages/ListUsers.php index 4e61d016..210200bb 100644 --- a/app-modules/events/src/Filament/Admin/Resources/Talks/Pages/ListTalks.php +++ b/app-modules/panel-admin/src/Filament/Resources/Users/Pages/ListUsers.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace He4rt\Events\Filament\Admin\Resources\Talks\Pages; +namespace He4rt\PanelAdmin\Filament\Resources\Users\Pages; use Filament\Actions\CreateAction; use Filament\Resources\Pages\ListRecords; -use He4rt\Events\Filament\Admin\Resources\Talks\TalkResource; +use He4rt\PanelAdmin\Filament\Resources\Users\UserResource; -class ListTalks extends ListRecords +class ListUsers extends ListRecords { - protected static string $resource = TalkResource::class; + protected static string $resource = UserResource::class; protected function getHeaderActions(): array { diff --git a/app-modules/panel-admin/src/Filament/Resources/Users/RelationManagers/ProvidersRelationManager.php b/app-modules/panel-admin/src/Filament/Resources/Users/RelationManagers/ProvidersRelationManager.php new file mode 100644 index 00000000..82826927 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Users/RelationManagers/ProvidersRelationManager.php @@ -0,0 +1,45 @@ +columns([ + TextColumn::make('provider') + ->badge(), + TextColumn::make('external_account_id') + ->copyable() + ->label('External ID'), + TextColumn::make('credentials_type') + ->badge(), + TextColumn::make('connected_at') + ->dateTime() + ->sortable(), + TextColumn::make('disconnected_at') + ->dateTime() + ->placeholder('Connected'), + ]) + ->actions([ + Action::make('disconnect') + ->icon(Heroicon::NoSymbol) + ->color('danger') + ->visible(fn ($record) => $record->disconnected_at === null) + ->requiresConfirmation() + ->action(fn ($record) => $record->update(['disconnected_at' => now()])) + ->successNotificationTitle('Identity disconnected'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Users/Schemas/UserForm.php b/app-modules/panel-admin/src/Filament/Resources/Users/Schemas/UserForm.php new file mode 100644 index 00000000..7a3ea61e --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Users/Schemas/UserForm.php @@ -0,0 +1,98 @@ +columns(1) + ->components([ + Tabs::make('User') + ->tabs([ + Tab::make('General') + ->icon(Heroicon::User) + ->schema([ + TextInput::make('username') + ->required() + ->minLength(3) + ->maxLength(50) + ->unique('users', 'username', ignoreRecord: true) + ->autocomplete('off'), + TextInput::make('name') + ->required() + ->maxLength(100) + ->unique('users', 'name', ignoreRecord: true), + TextInput::make('email') + ->nullable() + ->email() + ->maxLength(255), + TextInput::make('password') + ->password() + ->revealable() + ->required(fn (string $operation): bool => $operation === 'create') + ->nullable() + ->minLength(8) + ->dehydrateStateUsing(fn ($state) => bcrypt($state)) + ->dehydrated(fn ($state) => filled($state)), + Toggle::make('is_donator') + ->default(false), + ]), + Tab::make('Address') + ->icon(Heroicon::MapPin) + ->schema([ + Group::make() + ->relationship('address') + ->schema([ + TextInput::make('country') + ->nullable() + ->maxLength(255), + TextInput::make('state') + ->nullable() + ->maxLength(255), + TextInput::make('city') + ->nullable() + ->maxLength(255), + TextInput::make('zip_code') + ->nullable() + ->maxLength(20), + ]), + ]), + Tab::make('Information') + ->icon(Heroicon::InformationCircle) + ->schema([ + Group::make() + ->relationship('information') + ->schema([ + TextInput::make('nickname') + ->nullable(), + TextInput::make('linkedin_url') + ->nullable() + ->url(), + TextInput::make('github_url') + ->nullable() + ->url(), + DatePicker::make('birthdate') + ->nullable(), + Textarea::make('about') + ->nullable() + ->rows(3), + ]), + ]), + ]), + ]); + } +} diff --git a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Tables/MeetingTypesTable.php b/app-modules/panel-admin/src/Filament/Resources/Users/Tables/UsersTable.php similarity index 64% rename from app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Tables/MeetingTypesTable.php rename to app-modules/panel-admin/src/Filament/Resources/Users/Tables/UsersTable.php index 27d63bae..31f6e45e 100644 --- a/app-modules/community/src/Meeting/Filament/Resources/MeetingTypes/Tables/MeetingTypesTable.php +++ b/app-modules/panel-admin/src/Filament/Resources/Users/Tables/UsersTable.php @@ -2,40 +2,40 @@ declare(strict_types=1); -namespace He4rt\Community\Meeting\Filament\Resources\MeetingTypes\Tables; +namespace He4rt\PanelAdmin\Filament\Resources\Users\Tables; use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; +use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\TernaryFilter; use Filament\Tables\Table; -class MeetingTypesTable +class UsersTable { public static function configure(Table $table): Table { return $table ->columns([ + TextColumn::make('username') + ->searchable() + ->sortable(), TextColumn::make('name') ->searchable(), - TextColumn::make('week_day') - ->numeric() - ->sortable(), - TextColumn::make('start_at') - ->time() - ->sortable(), + TextColumn::make('email') + ->searchable(), + IconColumn::make('is_donator') + ->boolean(), TextColumn::make('created_at') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('updated_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ - + TernaryFilter::make('is_donator'), ]) + ->defaultSort('created_at', 'desc') ->recordActions([ EditAction::make(), ]) diff --git a/app-modules/panel-admin/src/Filament/Resources/Users/UserResource.php b/app-modules/panel-admin/src/Filament/Resources/Users/UserResource.php new file mode 100644 index 00000000..48bc1c65 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Users/UserResource.php @@ -0,0 +1,72 @@ + + */ + public static function getGloballySearchableAttributes(): array + { + return ['username', 'name', 'email']; + } + + public static function form(Schema $schema): Schema + { + return UserForm::configure($schema); + } + + public static function table(Table $table): Table + { + return UsersTable::configure($table); + } + + /** + * @return array + */ + public static function getRelations(): array + { + return [ + ProvidersRelationManager::class, + ]; + } + + /** + * @return array + */ + public static function getPages(): array + { + return [ + 'index' => ListUsers::route('/'), + 'create' => CreateUser::route('/create'), + 'edit' => EditUser::route('/{record}/edit'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Voices/Pages/ListVoices.php b/app-modules/panel-admin/src/Filament/Resources/Voices/Pages/ListVoices.php new file mode 100644 index 00000000..960c3544 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Voices/Pages/ListVoices.php @@ -0,0 +1,13 @@ +defaultSort('created_at', 'desc') + ->columns([ + TextColumn::make('provider.user.username') + ->label('User'), + TextColumn::make('channel_name') + ->searchable(), + TextColumn::make('state') + ->badge(), + TextColumn::make('obtained_experience') + ->numeric(0) + ->label('XP'), + TextColumn::make('created_at') + ->dateTime() + ->sortable(), + ]) + ->filters([ + SelectFilter::make('state') + ->options([ + 'disabled' => 'Disabled', + 'muted' => 'Muted', + 'unmuted' => 'Unmuted', + ]), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Voices/VoiceResource.php b/app-modules/panel-admin/src/Filament/Resources/Voices/VoiceResource.php new file mode 100644 index 00000000..0b872515 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Voices/VoiceResource.php @@ -0,0 +1,52 @@ +components([]); + } + + public static function table(Table $table): Table + { + return VoicesTable::configure($table); + } + + public static function getRelations(): array + { + return []; + } + + public static function getPages(): array + { + return [ + 'index' => ListVoices::route('/'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Wallets/Pages/ListWallets.php b/app-modules/panel-admin/src/Filament/Resources/Wallets/Pages/ListWallets.php new file mode 100644 index 00000000..4dac8eb1 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Wallets/Pages/ListWallets.php @@ -0,0 +1,13 @@ +defaultSort('created_at', 'desc') + ->columns([ + TextColumn::make('type') + ->badge(), + TextColumn::make('amount') + ->numeric(0) + ->color(fn (int $state): string => $state >= 0 ? 'success' : 'danger'), + TextColumn::make('balance_after') + ->numeric(0), + TextColumn::make('description') + ->limit(50) + ->tooltip(fn ($record) => $record->description), + TextColumn::make('created_at') + ->dateTime() + ->sortable(), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Wallets/Tables/WalletsTable.php b/app-modules/panel-admin/src/Filament/Resources/Wallets/Tables/WalletsTable.php new file mode 100644 index 00000000..e20d3921 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Wallets/Tables/WalletsTable.php @@ -0,0 +1,64 @@ +columns([ + TextColumn::make('owner_type') + ->badge() + ->color('gray') + ->formatStateUsing(fn (string $state) => class_basename($state)), + TextColumn::make('currency') + ->badge(), + TextColumn::make('balance') + ->numeric(0) + ->sortable(), + TextColumn::make('transactions_count') + ->counts('transactions') + ->label('Transactions'), + ]) + ->filters([ + SelectFilter::make('currency') + ->options(Currency::class), + ]) + ->recordActions([ + Action::make('credit') + ->icon(Heroicon::Plus) + ->color('success') + ->form([ + TextInput::make('amount') + ->required() + ->integer() + ->minValue(1), + TextInput::make('description') + ->nullable() + ->maxLength(255), + ]) + ->action(function ($record, array $data): void { + $record->increment('balance', (int) $data['amount']); + $record->transactions()->create([ + 'type' => TransactionType::Reward, + 'amount' => (int) $data['amount'], + 'balance_after' => $record->fresh()->balance, + 'description' => $data['description'] ?? 'Admin credit', + ]); + }) + ->successNotificationTitle('Wallet credited'), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Resources/Wallets/WalletResource.php b/app-modules/panel-admin/src/Filament/Resources/Wallets/WalletResource.php new file mode 100644 index 00000000..d5dad3f1 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Resources/Wallets/WalletResource.php @@ -0,0 +1,51 @@ +components([]); + } + + public static function table(Table $table): Table + { + return WalletsTable::configure($table); + } + + public static function getRelations(): array + { + return [ + TransactionsRelationManager::class, + ]; + } + + public static function getPages(): array + { + return [ + 'index' => ListWallets::route('/'), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Widgets/ActivityChart.php b/app-modules/panel-admin/src/Filament/Widgets/ActivityChart.php new file mode 100644 index 00000000..707f1156 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Widgets/ActivityChart.php @@ -0,0 +1,43 @@ +map(fn (int $i) => Date::today()->subDays($i)); + + $counts = Message::query() + ->where('sent_at', '>=', Date::today()->subDays(30)) + ->selectRaw('DATE(sent_at) as date, COUNT(*) as count') + ->groupByRaw('DATE(sent_at)') + ->pluck('count', 'date'); + + return [ + 'datasets' => [ + [ + 'label' => 'Messages', + 'data' => $days->map(fn (Carbon $day) => $counts->get($day->toDateString(), 0))->toArray(), + ], + ], + 'labels' => $days->map(fn (Carbon $day) => $day->format('M d'))->all(), + ]; + } + + protected function getType(): string + { + return 'line'; + } +} diff --git a/app-modules/panel-admin/src/Filament/Widgets/EconomyStatsOverview.php b/app-modules/panel-admin/src/Filament/Widgets/EconomyStatsOverview.php new file mode 100644 index 00000000..d0a8ebd9 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Widgets/EconomyStatsOverview.php @@ -0,0 +1,27 @@ +where('currency', Currency::Coin)->sum('balance')) + ->icon(Heroicon::CurrencyDollar), + Stat::make('Transactions Today', Transaction::query()->whereDate('created_at', today())->count()) + ->icon(Heroicon::ReceiptPercent), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Widgets/PendingReviewsWidget.php b/app-modules/panel-admin/src/Filament/Widgets/PendingReviewsWidget.php new file mode 100644 index 00000000..99c346b5 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Widgets/PendingReviewsWidget.php @@ -0,0 +1,37 @@ +query( + Feedback::query() + ->whereDoesntHave('review') + ->latest() + ->limit(10) + ) + ->columns([ + TextColumn::make('sender.username')->label('From'), + TextColumn::make('target.username')->label('To'), + TextColumn::make('type')->badge(), + TextColumn::make('message')->limit(60), + TextColumn::make('created_at')->dateTime(), + ]); + } +} diff --git a/app-modules/panel-admin/src/Filament/Widgets/PlatformStatsOverview.php b/app-modules/panel-admin/src/Filament/Widgets/PlatformStatsOverview.php new file mode 100644 index 00000000..87d357e3 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Widgets/PlatformStatsOverview.php @@ -0,0 +1,32 @@ +count()) + ->icon(Heroicon::Users), + Stat::make('Active Tenants', Tenant::query()->where('active', true)->count()) + ->icon(Heroicon::BuildingOffice), + Stat::make('Total Characters', Character::query()->count()) + ->icon(Heroicon::UserCircle), + Stat::make('Active Events', EventModel::query()->where('active', true)->count()) + ->icon(Heroicon::Ticket), + ]; + } +} diff --git a/app-modules/panel-admin/src/Filament/Widgets/SeasonStatsOverview.php b/app-modules/panel-admin/src/Filament/Widgets/SeasonStatsOverview.php new file mode 100644 index 00000000..87a47779 --- /dev/null +++ b/app-modules/panel-admin/src/Filament/Widgets/SeasonStatsOverview.php @@ -0,0 +1,32 @@ +whereNull('ended_at')->first(); + + return [ + Stat::make('Current Season', $currentSeason?->name ?? 'None') + ->icon(Heroicon::Flag), + Stat::make('Total XP Distributed', Number::abbreviate(Character::query()->sum('experience'))) + ->icon(Heroicon::ArrowTrendingUp), + Stat::make('Badges Claimed', DB::table('characters_badges')->count()) + ->icon(Heroicon::Star), + ]; + } +} diff --git a/app-modules/panel-admin/src/PanelAdminServiceProvider.php b/app-modules/panel-admin/src/PanelAdminServiceProvider.php new file mode 100644 index 00000000..665db436 --- /dev/null +++ b/app-modules/panel-admin/src/PanelAdminServiceProvider.php @@ -0,0 +1,14 @@ +assertTrue(true); + } +} diff --git a/app-modules/panel-admin/tests/Feature/Resources/Badges/CreateBadgeTest.php b/app-modules/panel-admin/tests/Feature/Resources/Badges/CreateBadgeTest.php new file mode 100644 index 00000000..e8f51ea4 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Badges/CreateBadgeTest.php @@ -0,0 +1,51 @@ +value)); + $this->actingAsAdmin(); + $this->tenant = Tenant::query()->first(); +}); + +it('can render', function (): void { + livewire(CreateBadge::class)->assertOk(); +}); + +it('can create a badge', function (): void { + livewire(CreateBadge::class) + ->fillForm([ + 'tenant_id' => $this->tenant->getKey(), + 'name' => 'Test Badge', + 'description' => 'A test badge', + 'redeem_code' => 'TEST123', + 'provider' => IdentityProvider::Discord->value, + 'active' => true, + ]) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas(Badge::class, [ + 'name' => 'Test Badge', + 'redeem_code' => 'TEST123', + ]); +}); + +it('validates form data', function (string $field, mixed $value, string $rule): void { + livewire(CreateBadge::class) + ->fillForm([$field => $value]) + ->call('create') + ->assertHasFormErrors([$field => $rule]); +})->with([ + 'name is required' => ['name', '', 'required'], + 'redeem_code is required' => ['redeem_code', '', 'required'], +]); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Badges/ListBadgesTest.php b/app-modules/panel-admin/tests/Feature/Resources/Badges/ListBadgesTest.php new file mode 100644 index 00000000..81467e52 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Badges/ListBadgesTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListBadges::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Characters/ListCharactersTest.php b/app-modules/panel-admin/tests/Feature/Resources/Characters/ListCharactersTest.php new file mode 100644 index 00000000..3f737179 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Characters/ListCharactersTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListCharacters::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/EventModels/CreateEventModelTest.php b/app-modules/panel-admin/tests/Feature/Resources/EventModels/CreateEventModelTest.php new file mode 100644 index 00000000..3e47ac7c --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/EventModels/CreateEventModelTest.php @@ -0,0 +1,63 @@ +value)); + $this->actingAsAdmin(); + $this->tenant = Tenant::query()->first(); +}); + +it('can render', function (): void { + livewire(CreateEventModel::class)->assertOk(); +}); + +it('can create an event', function (): void { + livewire(CreateEventModel::class) + ->fillForm([ + 'tenant_id' => $this->tenant->getKey(), + 'title' => 'event title', + 'slug' => 'event-slug', + 'description' => 'event description', + 'location' => 'event location', + 'max_attendees' => 5, + 'event_type' => EventTypeEnum::Workshop->value, + 'active' => true, + 'event_at' => today(), + 'start_at' => today(), + 'end_at' => Date::tomorrow(), + ]) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas(EventModel::class, [ + 'title' => 'event title', + 'slug' => 'event-slug', + ]); +}); + +it('validates form data', function (string $field, mixed $value, string $rule): void { + livewire(CreateEventModel::class) + ->fillForm([$field => $value]) + ->call('create') + ->assertHasFormErrors([$field => $rule]); +})->with([ + 'title is required' => ['title', '', 'required'], + 'title min 5' => ['title', 'ab', 'min'], + 'location is required' => ['location', '', 'required'], + 'location min 5' => ['location', 'ab', 'min'], + 'max_attendees is required' => ['max_attendees', null, 'required'], + 'event_at is required' => ['event_at', null, 'required'], + 'start_at is required' => ['start_at', null, 'required'], + 'end_at is required' => ['end_at', null, 'required'], +]); diff --git a/app-modules/panel-admin/tests/Feature/Resources/EventModels/ListEventModelsTest.php b/app-modules/panel-admin/tests/Feature/Resources/EventModels/ListEventModelsTest.php new file mode 100644 index 00000000..907dd073 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/EventModels/ListEventModelsTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListEventModels::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Feedback/ListFeedbackTest.php b/app-modules/panel-admin/tests/Feature/Resources/Feedback/ListFeedbackTest.php new file mode 100644 index 00000000..9594ff20 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Feedback/ListFeedbackTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListFeedback::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/MeetingTypes/CreateMeetingTypeTest.php b/app-modules/panel-admin/tests/Feature/Resources/MeetingTypes/CreateMeetingTypeTest.php new file mode 100644 index 00000000..e3e9ff19 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/MeetingTypes/CreateMeetingTypeTest.php @@ -0,0 +1,28 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(CreateMeetingType::class)->assertOk(); +}); + +it('validates form data', function (string $field, mixed $value, string $rule): void { + livewire(CreateMeetingType::class) + ->fillForm([$field => $value]) + ->call('create') + ->assertHasFormErrors([$field => $rule]); +})->with([ + 'name is required' => ['name', '', 'required'], + 'week_day is required' => ['week_day', null, 'required'], +]); diff --git a/app-modules/panel-admin/tests/Feature/Resources/MeetingTypes/ListMeetingTypesTest.php b/app-modules/panel-admin/tests/Feature/Resources/MeetingTypes/ListMeetingTypesTest.php new file mode 100644 index 00000000..b32b94ac --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/MeetingTypes/ListMeetingTypesTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListMeetingTypes::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Meetings/ListMeetingsTest.php b/app-modules/panel-admin/tests/Feature/Resources/Meetings/ListMeetingsTest.php new file mode 100644 index 00000000..0e42cf02 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Meetings/ListMeetingsTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListMeetings::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Messages/ListMessagesTest.php b/app-modules/panel-admin/tests/Feature/Resources/Messages/ListMessagesTest.php new file mode 100644 index 00000000..5dcec28d --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Messages/ListMessagesTest.php @@ -0,0 +1,24 @@ +value)); + $this->actingAsAdmin(); +}); + +it('is registered in admin panel', function (): void { + expect(Filament::getResources()) + ->toContain(MessageResource::class); +}); + +it('can render', function (): void { + livewire(ListMessages::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Seasons/CreateSeasonTest.php b/app-modules/panel-admin/tests/Feature/Resources/Seasons/CreateSeasonTest.php new file mode 100644 index 00000000..6647650b --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Seasons/CreateSeasonTest.php @@ -0,0 +1,48 @@ +value)); + $this->actingAsAdmin(); + $this->tenant = Tenant::query()->first(); +}); + +it('can render', function (): void { + livewire(CreateSeason::class)->assertOk(); +}); + +it('can create a season', function (): void { + livewire(CreateSeason::class) + ->fillForm([ + 'tenant_id' => $this->tenant->getKey(), + 'name' => 'Season 1', + 'description' => 'First season', + 'started_at' => now(), + 'ended_at' => now()->addMonths(3), + ]) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas(Season::class, [ + 'name' => 'Season 1', + ]); +}); + +it('validates form data', function (string $field, mixed $value, string $rule): void { + livewire(CreateSeason::class) + ->fillForm([$field => $value]) + ->call('create') + ->assertHasFormErrors([$field => $rule]); +})->with([ + 'name is required' => ['name', '', 'required'], + 'started_at is required' => ['started_at', null, 'required'], +]); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Seasons/ListSeasonsTest.php b/app-modules/panel-admin/tests/Feature/Resources/Seasons/ListSeasonsTest.php new file mode 100644 index 00000000..224545ea --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Seasons/ListSeasonsTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListSeasons::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Sponsors/CreateSponsorTest.php b/app-modules/panel-admin/tests/Feature/Resources/Sponsors/CreateSponsorTest.php new file mode 100644 index 00000000..5e207089 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Sponsors/CreateSponsorTest.php @@ -0,0 +1,36 @@ +value)); + $this->actingAsAdmin(); + $this->tenant = Tenant::query()->first(); +}); + +it('can render', function (): void { + livewire(CreateSponsor::class)->assertOk(); +}); + +it('can create a sponsor', function (): void { + livewire(CreateSponsor::class) + ->fillForm([ + 'tenant_id' => $this->tenant->getKey(), + 'name' => 'Test Sponsor', + 'homepage_url' => 'https://example.com', + ]) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas(Sponsor::class, [ + 'name' => 'Test Sponsor', + ]); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Sponsors/ListSponsorsTest.php b/app-modules/panel-admin/tests/Feature/Resources/Sponsors/ListSponsorsTest.php new file mode 100644 index 00000000..429fb6ff --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Sponsors/ListSponsorsTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListSponsors::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Tenants/CreateTenantTest.php b/app-modules/panel-admin/tests/Feature/Resources/Tenants/CreateTenantTest.php new file mode 100644 index 00000000..a6008fde --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Tenants/CreateTenantTest.php @@ -0,0 +1,49 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(CreateTenant::class)->assertOk(); +}); + +it('can create a tenant', function (): void { + $owner = User::factory()->create(); + + livewire(CreateTenant::class) + ->fillForm([ + 'name' => 'Test Tenant', + 'slug' => 'test-tenant', + 'owner_id' => $owner->getKey(), + 'active' => true, + ]) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas(Tenant::class, [ + 'name' => 'Test Tenant', + 'slug' => 'test-tenant', + ]); +}); + +it('validates form data', function (string $field, mixed $value, string $rule): void { + livewire(CreateTenant::class) + ->fillForm([$field => $value]) + ->call('create') + ->assertHasFormErrors([$field => $rule]); +})->with([ + 'name is required' => ['name', '', 'required'], + 'slug is required' => ['slug', '', 'required'], +]); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Tenants/EditTenantTest.php b/app-modules/panel-admin/tests/Feature/Resources/Tenants/EditTenantTest.php new file mode 100644 index 00000000..6cd31023 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Tenants/EditTenantTest.php @@ -0,0 +1,22 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + $tenant = Tenant::query()->first(); + + livewire(EditTenant::class, ['record' => $tenant->getRouteKey()]) + ->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Tenants/ListTenantsTest.php b/app-modules/panel-admin/tests/Feature/Resources/Tenants/ListTenantsTest.php new file mode 100644 index 00000000..4fb114f2 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Tenants/ListTenantsTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListTenants::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Tenants/TenantResourceTest.php b/app-modules/panel-admin/tests/Feature/Resources/Tenants/TenantResourceTest.php new file mode 100644 index 00000000..1399a05f --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Tenants/TenantResourceTest.php @@ -0,0 +1,17 @@ +value)); + $this->actingAsAdmin(); +}); + +it('is registered in admin panel', function (): void { + expect(Filament::getResources()) + ->toContain(TenantResource::class); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Users/CreateUserTest.php b/app-modules/panel-admin/tests/Feature/Resources/Users/CreateUserTest.php new file mode 100644 index 00000000..58626c04 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Users/CreateUserTest.php @@ -0,0 +1,48 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(CreateUser::class)->assertOk(); +}); + +it('can create a user', function (): void { + livewire(CreateUser::class) + ->fillForm([ + 'username' => 'newuser', + 'name' => 'New User', + 'email' => 'test@example.com', + 'password' => 'password123', + ]) + ->call('create') + ->assertHasNoFormErrors(); + + $this->assertDatabaseHas(User::class, [ + 'username' => 'newuser', + 'name' => 'New User', + 'email' => 'test@example.com', + ]); +}); + +it('validates form data', function (string $field, mixed $value, string $rule): void { + livewire(CreateUser::class) + ->fillForm([$field => $value]) + ->call('create') + ->assertHasFormErrors([$field => $rule]); +})->with([ + 'username is required' => ['username', '', 'required'], + 'username min 3' => ['username', 'ab', 'min'], + 'name is required' => ['name', '', 'required'], +]); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Users/EditUserTest.php b/app-modules/panel-admin/tests/Feature/Resources/Users/EditUserTest.php new file mode 100644 index 00000000..abc564a6 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Users/EditUserTest.php @@ -0,0 +1,38 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + $user = User::factory()->create(); + + livewire(EditUser::class, ['record' => $user->getRouteKey()]) + ->assertOk(); +}); + +it('can update a user', function (): void { + $user = User::factory()->create(); + + livewire(EditUser::class, ['record' => $user->getRouteKey()]) + ->fillForm([ + 'username' => 'updated-user', + 'name' => 'Updated Name', + ]) + ->call('save') + ->assertHasNoFormErrors(); + + expect($user->fresh()) + ->username->toBe('updated-user') + ->name->toBe('Updated Name'); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Users/ListUsersTest.php b/app-modules/panel-admin/tests/Feature/Resources/Users/ListUsersTest.php new file mode 100644 index 00000000..69e6d6fd --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Users/ListUsersTest.php @@ -0,0 +1,18 @@ +value)); + $this->actingAsAdmin(); +}); + +it('can render', function (): void { + livewire(ListUsers::class)->assertOk(); +}); diff --git a/app-modules/panel-admin/tests/Feature/Resources/Users/UserResourceTest.php b/app-modules/panel-admin/tests/Feature/Resources/Users/UserResourceTest.php new file mode 100644 index 00000000..c6d79ff9 --- /dev/null +++ b/app-modules/panel-admin/tests/Feature/Resources/Users/UserResourceTest.php @@ -0,0 +1,17 @@ +value)); + $this->actingAsAdmin(); +}); + +it('is registered in admin panel', function (): void { + expect(Filament::getResources()) + ->toContain(UserResource::class); +}); diff --git a/app/Filament/Shared/RelationManagers/EventsRelationManager.php b/app/Filament/Shared/RelationManagers/EventsRelationManager.php deleted file mode 100644 index 7bfb089c..00000000 --- a/app/Filament/Shared/RelationManagers/EventsRelationManager.php +++ /dev/null @@ -1,45 +0,0 @@ -events->where('active', true)->count()); - } - - public function form(Schema $schema): Schema - { - return EventForm::configure($schema); - } - - public function table(Table $table): Table - { - return EventsTable::configure($table); - } - - protected function getTableHeaderActions(): array - { - return [ - CreateAction::make() - ->visible(fn ($livewire) => $livewire->pageClass !== EditSponsor::class), - ]; - } -} diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 666eeee8..c32a3e10 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -35,28 +35,23 @@ public function panel(Panel $panel): Panel ...Color::all(), ]) ->viteTheme('resources/css/filament/admin/theme.css') - ->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources') - ->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages') + ->discoverResources(in: modules_path('panel-admin/src/Filament/Resources'), for: 'He4rt\\PanelAdmin\\Filament\\Resources') + ->discoverPages(in: modules_path('panel-admin/src/Filament/Pages'), for: 'He4rt\\PanelAdmin\\Filament\\Pages') + ->discoverWidgets(in: modules_path('panel-admin/src/Filament/Widgets'), for: 'He4rt\\PanelAdmin\\Filament\\Widgets') + ->discoverClusters(in: modules_path('panel-admin/src/Filament/Clusters'), for: 'He4rt\\PanelAdmin\\Filament\\Clusters') ->pages([ Dashboard::class, ]) - ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets') ->navigationGroups([ - NavigationGroup::make() - ->label('Administration'), - - NavigationGroup::make() - ->label('Events'), - - NavigationGroup::make() - ->label('Gamefication'), - - NavigationGroup::make() - ->label('Meetings'), - - NavigationGroup::make() - ->label('General'), + NavigationGroup::make('Community'), + NavigationGroup::make('Gamification'), + NavigationGroup::make('Economy'), + NavigationGroup::make('Events'), + NavigationGroup::make('Activity'), + NavigationGroup::make('Meetings'), + NavigationGroup::make('Moderation'), ]) + ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets') ->middleware([ EncryptCookies::class, AddQueuedCookiesToResponse::class, diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php index c87f5c25..dd3dd44f 100644 --- a/bootstrap/helpers.php +++ b/bootstrap/helpers.php @@ -2,9 +2,17 @@ declare(strict_types=1); -if (! function_exists('module_path')) { - function module_path(string $module, string $path = ''): string +if (! function_exists('modules_path')) { + /** + * Get the path to the modules' folder. + * + * @see https://github.com/InterNACHI/modular/pull/99 + */ + function modules_path(string $path = ''): string { - return base_path(sprintf('app-modules/%s/%s', str($module)->lower()->kebab(), $path)); + $directory_name = config('app-modules.modules_directory', 'app-modules'); + $path = base_path($directory_name.DIRECTORY_SEPARATOR.mb_ltrim($path, '/\\')); + + return str_replace('\\', '/', mb_rtrim($path, '/\\')); } } diff --git a/composer.json b/composer.json index 3e28b4ec..ef32d8ac 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "he4rt/identity": ">=1", "he4rt/integration-discord": ">=1", "he4rt/integration-twitch": ">=1", + "he4rt/panel-admin": "^1.0", "he4rt/portal": ">=1", "internachi/modular": "dev-main#ad95fe9", "laracord/framework": "dev-next", diff --git a/composer.lock b/composer.lock index 20ffce7f..b0dd74d6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f3be323ab4f835734901b5f89dc34c61", + "content-hash": "f49064900ba918d2aababcfdfe950f2f", "packages": [ { "name": "blade-ui-kit/blade-heroicons", @@ -3417,6 +3417,38 @@ "relative": true } }, + { + "name": "he4rt/panel-admin", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/panel-admin", + "reference": "cbdee07c016511ad846726a0d2d08cb06e3f6513" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "He4rt\\PanelAdmin\\PanelAdminServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "He4rt\\PanelAdmin\\": "src/", + "He4rt\\PanelAdmin\\Tests\\": "tests/", + "He4rt\\PanelAdmin\\Database\\Factories\\": "database/factories/", + "He4rt\\PanelAdmin\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, { "name": "he4rt/portal", "version": "1.0", diff --git a/phpstan.ignore.neon b/phpstan.ignore.neon index c2432088..b699487f 100644 --- a/phpstan.ignore.neon +++ b/phpstan.ignore.neon @@ -19,3 +19,8 @@ parameters: identifier: method.notFound count: 1 path: app/Providers/FilamentServiceProvider.php + + - message: '#^Function module_path not found\.$#' + identifier: function.notFound + count: 1 + path: app/Providers/FilamentServiceProvider.php