From 9bd83c0339fa029ddbdc65960523d4ef3f47dbd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20Ioni=C8=9B=C4=83?= Date: Thu, 21 Dec 2023 13:35:44 +0200 Subject: [PATCH] wip --- app/Enums/OrganizationType.php | 23 ++ .../Admin/Resources/OrganizationResource.php | 235 ++++++++++++++++++ .../Pages/CreateOrganization.php | 15 ++ .../Pages/EditOrganization.php | 19 ++ .../Pages/ListOrganizations.php | 19 ++ .../Pages/ViewOrganization.php | 26 ++ .../RelationManagers/UsersRelationManager.php | 60 +++++ app/Filament/Admin/Resources/UserResource.php | 92 +++++++ .../UserResource/Pages/CreateUser.php | 12 + .../Resources/UserResource/Pages/EditUser.php | 19 ++ .../UserResource/Pages/ListUsers.php | 19 ++ app/Filament/Admin/Widgets/StatsWidget.php | 25 ++ .../MediaLibrary/TenantPathGenerator.php | 13 +- .../Pages/Tenancy/EditOrganizationProfile.php | 73 +----- .../Organizations/Resources/UserResource.php | 4 +- app/Models/Organization.php | 5 + database/factories/OrganizationFactory.php | 2 + lang/ro/enum.php | 6 + lang/ro/user.php | 5 + 19 files changed, 594 insertions(+), 78 deletions(-) create mode 100644 app/Enums/OrganizationType.php create mode 100644 app/Filament/Admin/Resources/OrganizationResource.php create mode 100644 app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php create mode 100644 app/Filament/Admin/Resources/OrganizationResource/Pages/EditOrganization.php create mode 100644 app/Filament/Admin/Resources/OrganizationResource/Pages/ListOrganizations.php create mode 100644 app/Filament/Admin/Resources/OrganizationResource/Pages/ViewOrganization.php create mode 100644 app/Filament/Admin/Resources/OrganizationResource/RelationManagers/UsersRelationManager.php create mode 100644 app/Filament/Admin/Resources/UserResource.php create mode 100644 app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php create mode 100644 app/Filament/Admin/Resources/UserResource/Pages/EditUser.php create mode 100644 app/Filament/Admin/Resources/UserResource/Pages/ListUsers.php create mode 100644 app/Filament/Admin/Widgets/StatsWidget.php diff --git a/app/Enums/OrganizationType.php b/app/Enums/OrganizationType.php new file mode 100644 index 00000000..a074d494 --- /dev/null +++ b/app/Enums/OrganizationType.php @@ -0,0 +1,23 @@ +schema([ + Infolists\Components\Group::make() + ->maxWidth('3xl') + ->schema([ + Infolists\Components\Section::make() + ->columns(2) + ->schema([ + Infolists\Components\TextEntry::make('name') + ->label(__('organization.field.name')), + + Infolists\Components\TextEntry::make('short_name') + ->label(__('organization.field.short_name')), + + EnumEntry::make('type') + ->label(__('organization.field.type')), + + Infolists\Components\TextEntry::make('cif') + ->label(__('organization.field.cif')), + + Infolists\Components\TextEntry::make('main_activity') + ->label(__('organization.field.main_activity')), + + Infolists\Components\TextEntry::make('phone') + ->label(__('organization.field.phone')), + + Infolists\Components\TextEntry::make('website') + ->label(__('organization.field.website')), + ]), + + Infolists\Components\Section::make(__('organization.section.location')) + ->columns(2) + ->schema([ + Infolists\Components\TextEntry::make('address') + ->label(__('organization.field.address')) + ->columnSpanFull(), + + // Infolists\Components\Location::make() + // ->city() + // ->required(), + ]), + + Infolists\Components\Section::make(__('organization.section.reprezentative')) + ->columns(2) + ->schema([ + Infolists\Components\TextEntry::make('reprezentative_name') + ->label(__('organization.field.reprezentative_name')), + + Infolists\Components\TextEntry::make('reprezentative_email') + ->label(__('organization.field.reprezentative_email')), + ]), + + Infolists\Components\Section::make(__('organization.field.logo')) + ->schema([ + Infolists\Components\SpatieMediaLibraryImageEntry::make('logo') + ->label(__('organization.field.logo')) + ->hiddenLabel() + ->collection('logo') + ->conversion('large') + ->columnSpanFull(), + ]), + ]), + ]); + } + + public static function form(Form $form): Form + { + return $form + ->schema([ + Forms\Components\Group::make() + ->maxWidth('3xl') + ->schema([ + Forms\Components\Section::make() + ->columns(2) + ->schema([ + Forms\Components\TextInput::make('name') + ->label(__('organization.field.name')), + + Forms\Components\TextInput::make('short_name') + ->label(__('organization.field.short_name')), + + Forms\Components\Select::make('type') + ->label(__('organization.field.type')) + ->options(OrganizationType::options()) + ->enum(OrganizationType::class), + + Forms\Components\TextInput::make('cif') + ->label(__('organization.field.cif')) + ->rule(new ValidCIF), + + Forms\Components\TextInput::make('main_activity') + ->label(__('organization.field.main_activity')), + + Forms\Components\TextInput::make('phone') + ->label(__('organization.field.phone')) + ->tel(), + + Forms\Components\TextInput::make('website') + ->label(__('organization.field.website')) + ->url(), + ]), + + Forms\Components\Section::make(__('organization.section.location')) + ->columns(2) + ->schema([ + Forms\Components\TextInput::make('address') + ->label(__('organization.field.address')) + ->maxLength(200) + ->columnSpanFull() + ->required(), + + Location::make() + ->city() + ->required(), + ]), + + Forms\Components\Section::make(__('organization.section.reprezentative')) + ->columns(2) + ->schema([ + Forms\Components\TextInput::make('reprezentative_name') + ->label(__('organization.field.reprezentative_name')), + + Forms\Components\TextInput::make('reprezentative_email') + ->label(__('organization.field.reprezentative_email')), + ]), + + Forms\Components\Section::make(__('organization.field.logo')) + ->schema([ + Forms\Components\SpatieMediaLibraryFileUpload::make('logo') + ->label(__('organization.field.logo')) + ->hiddenLabel() + ->image() + ->collection('logo') + ->conversion('large') + ->columnSpanFull(), + ]), + ]), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + SpatieMediaLibraryImageColumn::make('logo') + ->collection('logo') + ->conversion('thumb') + ->width(40) + ->height(40) + ->toggleable(), + + TextColumn::make('name') + ->label(__('organization.field.name')) + ->searchable(), + + TextColumn::make('short_name') + ->label(__('organization.field.short_name')) + ->toggleable() + ->searchable(), + + TextColumn::make('type') + ->label(__('organization.field.type')) + ->formatStateUsing(fn ($state) => $state?->label()) + ->toggleable(), + + TextColumn::make('cif') + ->label(__('organization.field.cif')) + ->toggleable() + ->searchable(), + ]) + ->filters([ + SelectFilter::make('type') + ->label(__('organization.field.type')) + ->options(OrganizationType::options()) + ->multiple(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + UsersRelationManager::class, + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListOrganizations::route('/'), + 'create' => Pages\CreateOrganization::route('/create'), + 'view' => Pages\ViewOrganization::route('/{record}'), + 'edit' => Pages\EditOrganization::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php b/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php new file mode 100644 index 00000000..e70abbc9 --- /dev/null +++ b/app/Filament/Admin/Resources/OrganizationResource/Pages/CreateOrganization.php @@ -0,0 +1,15 @@ +schema([ + Forms\Components\TextInput::make('full_name') + ->required() + ->maxLength(255), + ]); + } + + public function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('id'), + TextColumn::make('first_name'), + TextColumn::make('last_name'), + TextColumn::make('email'), + TextColumn::make('role'), + TextColumn::make('created_at'), + ]) + ->filters([ + // + ]) + ->headerActions([ + Tables\Actions\CreateAction::make(), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public function isReadOnly(): bool + { + return false; + } +} diff --git a/app/Filament/Admin/Resources/UserResource.php b/app/Filament/Admin/Resources/UserResource.php new file mode 100644 index 00000000..14f15021 --- /dev/null +++ b/app/Filament/Admin/Resources/UserResource.php @@ -0,0 +1,92 @@ +schema([ + // + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('first_name') + ->searchable(), + + TextColumn::make('last_name') + ->searchable(), + + TextColumn::make('organizations.name'), + + TextColumn::make('roles'), + + TextColumn::make('account_status'), + + TextColumn::make('last_login_at'), + ]) + ->filters([ + SelectFilter::make('organizations') + ->relationship('organizations', 'name') + ->multiple(), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListUsers::route('/'), + 'create' => Pages\CreateUser::route('/create'), + 'edit' => Pages\EditUser::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php b/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php new file mode 100644 index 00000000..330d3996 --- /dev/null +++ b/app/Filament/Admin/Resources/UserResource/Pages/CreateUser.php @@ -0,0 +1,12 @@ +count() + ), + ]; + } +} diff --git a/app/Filament/MediaLibrary/TenantPathGenerator.php b/app/Filament/MediaLibrary/TenantPathGenerator.php index bad8a1a3..1d523c51 100644 --- a/app/Filament/MediaLibrary/TenantPathGenerator.php +++ b/app/Filament/MediaLibrary/TenantPathGenerator.php @@ -4,6 +4,7 @@ namespace App\Filament\MediaLibrary; +use App\Models\Organization; use Spatie\MediaLibrary\MediaCollections\Models\Media; use Spatie\MediaLibrary\Support\PathGenerator\DefaultPathGenerator; @@ -11,11 +12,13 @@ class TenantPathGenerator extends DefaultPathGenerator { protected function getBasePath(Media $media): string { - return collect([ - 'org', - filament()->getTenant()->ulid, - $media->getKey(), - ]) + $ulid = filament()->getTenant()?->ulid; + + if (! $ulid && $media->model instanceof Organization) { + $ulid = $media->model->ulid; + } + + return collect(['org', $ulid, $media->getKey()]) ->filter() ->join(\DIRECTORY_SEPARATOR); } diff --git a/app/Filament/Organizations/Pages/Tenancy/EditOrganizationProfile.php b/app/Filament/Organizations/Pages/Tenancy/EditOrganizationProfile.php index 9394bb3e..063915f6 100644 --- a/app/Filament/Organizations/Pages/Tenancy/EditOrganizationProfile.php +++ b/app/Filament/Organizations/Pages/Tenancy/EditOrganizationProfile.php @@ -4,12 +4,7 @@ namespace App\Filament\Organizations\Pages\Tenancy; -use App\Forms\Components\Location; -use App\Rules\ValidCIF; -use Filament\Forms\Components\Section; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\SpatieMediaLibraryFileUpload; -use Filament\Forms\Components\TextInput; +use App\Filament\Admin\Resources\OrganizationResource; use Filament\Forms\Form; use Filament\Pages\Tenancy\EditTenantProfile; @@ -24,70 +19,6 @@ public static function getLabel(): string public function form(Form $form): Form { - return $form - ->schema([ - Section::make() - ->columns(2) - ->schema([ - TextInput::make('name') - ->label(__('organization.field.name')), - - TextInput::make('short_name') - ->label(__('organization.field.short_name')), - - Select::make('type') - ->label(__('organization.field.type')), - - TextInput::make('cif') - ->label(__('organization.field.cif')) - ->rule(new ValidCIF), - - TextInput::make('main_activity') - ->label(__('organization.field.main_activity')), - - TextInput::make('phone') - ->label(__('organization.field.phone')) - ->tel(), - - TextInput::make('website') - ->label(__('organization.field.website')) - ->url(), - ]), - - Section::make(__('organization.section.location')) - ->columns(2) - ->schema([ - TextInput::make('address') - ->label(__('organization.field.address')) - ->maxLength(200) - ->columnSpanFull() - ->required(), - - Location::make() - ->city() - ->required(), - ]), - - Section::make(__('organization.section.reprezentative')) - ->columns(2) - ->schema([ - TextInput::make('reprezentative_name') - ->label(__('organization.field.reprezentative_name')), - - TextInput::make('reprezentative_email') - ->label(__('organization.field.reprezentative_email')), - ]), - - Section::make(__('organization.field.logo')) - ->schema([ - SpatieMediaLibraryFileUpload::make('logo') - ->label(__('organization.field.logo')) - ->hiddenLabel() - ->image() - ->collection('logo') - ->conversion('large') - ->columnSpanFull(), - ]), - ]); + return OrganizationResource::form($form); } } diff --git a/app/Filament/Organizations/Resources/UserResource.php b/app/Filament/Organizations/Resources/UserResource.php index 0e3b32ee..e68eca69 100644 --- a/app/Filament/Organizations/Resources/UserResource.php +++ b/app/Filament/Organizations/Resources/UserResource.php @@ -34,12 +34,12 @@ public static function getNavigationLabel(): string public static function getModelLabel(): string { - return __('user.label.singular'); + return __('user.specialist_label.singular'); } public static function getPluralModelLabel(): string { - return __('user.label.plural'); + return __('user.specialist_label.plural'); } public static function form(Form $form): Form diff --git a/app/Models/Organization.php b/app/Models/Organization.php index e5840316..c7455570 100644 --- a/app/Models/Organization.php +++ b/app/Models/Organization.php @@ -8,6 +8,7 @@ use App\Concerns\HasLocation; use App\Concerns\HasSlug; use App\Concerns\HasUlid; +use App\Enums\OrganizationType; use Filament\Models\Contracts\HasAvatar; use Filament\Models\Contracts\HasCurrentTenantLabel; use Filament\Models\Contracts\HasName; @@ -43,6 +44,10 @@ class Organization extends Model implements HasAvatar, HasMedia, HasName, HasCur 'website', ]; + protected $casts = [ + 'type' => OrganizationType::class, + ]; + public function users(): MorphToMany { return $this->morphedByMany(User::class, 'model', 'model_has_organizations'); diff --git a/database/factories/OrganizationFactory.php b/database/factories/OrganizationFactory.php index c19992a2..542450fd 100644 --- a/database/factories/OrganizationFactory.php +++ b/database/factories/OrganizationFactory.php @@ -4,6 +4,7 @@ namespace Database\Factories; +use App\Enums\OrganizationType; use App\Models\Beneficiary; use App\Models\City; use App\Models\CommunityProfile; @@ -32,6 +33,7 @@ public function definition(): array return [ 'name' => $name, 'short_name' => preg_replace('/\b(\w)|./u', '$1', $name), + 'type' => fake()->randomElement(OrganizationType::values()), 'phone' => fake()->phoneNumber(), 'website' => fake()->url(), diff --git a/lang/ro/enum.php b/lang/ro/enum.php index ad358439..46c2c9fd 100644 --- a/lang/ro/enum.php +++ b/lang/ro/enum.php @@ -154,4 +154,10 @@ 'other' => 'Alt loc (specificați)', ], + 'organization_type' => [ + 'ngo' => 'Organizație non-profit', + 'public' => 'Instituție publică', + 'private' => 'Entitate privată', + ], + ]; diff --git a/lang/ro/user.php b/lang/ro/user.php index e1727fb2..ace48901 100644 --- a/lang/ro/user.php +++ b/lang/ro/user.php @@ -5,6 +5,11 @@ return [ 'label' => [ + 'singular' => 'utilizator', + 'plural' => 'Utilizatori', + ], + + 'specialist_label' => [ 'singular' => 'specialist', 'plural' => 'Specialiști', ],