diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index 07e1b64a..539ad047 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -9,6 +9,7 @@ use App\Filament\Resources\UserResource\RelationManagers\BadgesRelationManager; use App\Filament\Resources\UserResource\RelationManagers\DonationsRelationManager; use App\Filament\Resources\UserResource\RelationManagers\VolunteersRelationManager; +use App\Forms\Components\Link; use App\Models\User; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; @@ -51,6 +52,15 @@ public static function form(Form $form): Form return $form ->columns(1) ->schema([ + Link::make('organizatii')->type('organization') + ->label(__('user.labels.organization')) + ->inlineLabel() + ->hidden( + fn (callable $get) => UserRole::SUPERMANAGER->is($get('role')) || + UserRole::SUPERADMIN->is($get('role')) || + UserRole::USER->is($get('role')) + ) + ->columnSpanFull(), TextInput::make('name') ->label(__('user.name')) ->inlineLabel() @@ -75,7 +85,11 @@ public static function form(Form $form): Form Select::make('organization') ->label(__('user.organization')) ->relationship('organization', 'name') - ->hidden(fn (callable $get) => UserRole::ADMIN->is($get('role'))) + ->hidden( + fn (callable $get) => UserRole::SUPERMANAGER->is($get('role')) || + UserRole::SUPERADMIN->is($get('role')) || + UserRole::USER->is($get('role')) + ) ->searchable() ->inlineLabel() ->preload() @@ -87,6 +101,12 @@ public static function table(Table $table): Table { return $table ->columns([ + TextColumn::make('id') + ->formatStateUsing(function (User $record) { + return sprintf('#%d', $record->id); + }) + ->label(__('project.labels.id')) + ->sortable(), TextColumn::make('name') ->label(__('user.name')) ->searchable() @@ -123,6 +143,12 @@ public static function table(Table $table): Table true: fn (Builder $query) => $query->has('volunteer'), false: fn (Builder $query) => $query->doesntHave('volunteer'), ), + TernaryFilter::make('has_verified_email') + ->label(__('user.filters.has_verified_email')) + ->queries( + true: fn (Builder $query) => $query->whereHasVerifiedEmail(), + false: fn (Builder $query) => $query->whereDoesntHaveVerifiedEmail(), + ), ]) ->actions([ @@ -152,4 +178,9 @@ public static function getPages(): array 'edit' => Pages\EditUser::route('/{record}/edit'), ]; } + + protected static function getNavigationBadge(): ?string + { + return (string) static::$model::whereHasVerifiedEmail()->count(); + } } diff --git a/app/Filament/Resources/UserResource/RelationManagers/BadgesRelationManager.php b/app/Filament/Resources/UserResource/RelationManagers/BadgesRelationManager.php index 8706b2dc..ce1e2f98 100644 --- a/app/Filament/Resources/UserResource/RelationManagers/BadgesRelationManager.php +++ b/app/Filament/Resources/UserResource/RelationManagers/BadgesRelationManager.php @@ -11,7 +11,6 @@ use Filament\Tables; use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\TextColumn; -use Illuminate\Database\Eloquent\Model; class BadgesRelationManager extends RelationManager { @@ -29,11 +28,6 @@ protected function getTableHeading(): string return __('user.relations.heading.badges', ['count' => $this->getTableQuery()->count()]); } - public static function canViewForRecord(Model $record): bool - { - return $record->isDonor(); - } - public static function form(Form $form): Form { return $form diff --git a/app/Filament/Resources/UserResource/RelationManagers/DonationsRelationManager.php b/app/Filament/Resources/UserResource/RelationManagers/DonationsRelationManager.php index 592eb4e7..e8e5e0db 100644 --- a/app/Filament/Resources/UserResource/RelationManagers/DonationsRelationManager.php +++ b/app/Filament/Resources/UserResource/RelationManagers/DonationsRelationManager.php @@ -13,7 +13,6 @@ use Filament\Tables; use Filament\Tables\Columns\TextColumn; use Illuminate\Contracts\Support\Htmlable; -use Illuminate\Database\Eloquent\Model; class DonationsRelationManager extends RelationManager { @@ -31,11 +30,6 @@ protected function getTableHeading(): string | Htmlable | Closure | null return __('user.relations.heading.donations', ['count' => $this->getTableQuery()->count(), 'total' => $this->getTableQuery()->sum('amount')]); } - public static function canViewForRecord(Model $record): bool - { - return $record->isDonor(); - } - public static function form(Form $form): Form { return $form diff --git a/app/Filament/Resources/UserResource/RelationManagers/VolunteersRelationManager.php b/app/Filament/Resources/UserResource/RelationManagers/VolunteersRelationManager.php index 61f42733..532281b7 100644 --- a/app/Filament/Resources/UserResource/RelationManagers/VolunteersRelationManager.php +++ b/app/Filament/Resources/UserResource/RelationManagers/VolunteersRelationManager.php @@ -10,7 +10,6 @@ use Filament\Resources\Table; use Filament\Tables; use Filament\Tables\Columns\TextColumn; -use Illuminate\Database\Eloquent\Model; class VolunteersRelationManager extends RelationManager { @@ -18,11 +17,6 @@ class VolunteersRelationManager extends RelationManager protected static ?string $recordTitleAttribute = 'name'; - public static function canViewForRecord(Model $record): bool - { - return $record->isDonor(); - } - public static function getTitle(): string { return __('user.relations.volunteer'); diff --git a/app/Filament/Resources/VolunteerResource.php b/app/Filament/Resources/VolunteerResource.php index d76688cc..def34c3c 100644 --- a/app/Filament/Resources/VolunteerResource.php +++ b/app/Filament/Resources/VolunteerResource.php @@ -4,28 +4,81 @@ namespace App\Filament\Resources; +use App\Enums\VolunteerStatus; use App\Filament\Resources\VolunteerResource\Pages; -use App\Models\Volunteer; +use App\Models\VolunteerRequest; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\TextInput; use Filament\Resources\Form; use Filament\Resources\Resource; use Filament\Resources\Table; -use Filament\Tables; +use Filament\Tables\Actions\DeleteAction; +use Filament\Tables\Actions\EditAction; +use Filament\Tables\Actions\ViewAction; +use Filament\Tables\Columns\IconColumn; +use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\SelectFilter; +use Filament\Tables\Filters\TernaryFilter; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\HtmlString; +use Illuminate\Support\Str; class VolunteerResource extends Resource { - protected static ?string $model = Volunteer::class; - - protected static ?string $navigationGroup = 'Administrează'; + protected static ?string $model = VolunteerRequest::class; protected static ?int $navigationSort = 6; protected static ?string $navigationIcon = 'heroicon-o-collection'; + public static function getPluralLabel(): ?string + { + return __('volunteer.label.plural'); + } + + public static function getModelLabel(): string + { + return __('volunteer.label.singular'); + } + + protected static function getNavigationGroup(): ?string + { + return __('navigation.group.manage'); + } + + protected static function getNavigationBadge(): ?string + { + return (string) VolunteerRequest::where('status', VolunteerStatus::APPROVED)->count(); + } + public static function form(Form $form): Form { return $form ->schema([ - // + TextInput::make('name') + ->label(__('volunteer.form.name')) + ->formatStateUsing(fn ($record) => $record->volunteer->name) + ->disabled(), + TextInput::make('email') + ->label(__('volunteer.form.email')) + ->formatStateUsing(fn ($record) => $record->volunteer->email) + ->disabled(), + TextInput::make('volunteer.phone') + ->label(__('volunteer.form.phone')) + ->formatStateUsing(fn ($record) => $record->volunteer->phone) + ->disabled(), + Select::make('status') + ->label(__('volunteer.form.status')) + ->options(VolunteerStatus::options()) + ->required(), + Select::make('model_type') + ->label(__('volunteer.form.model_type')) + ->options([ + 'App\Models\Project' => 'Project', + 'App\Models\Organization' => 'Organization', + ]) + ->disabled(), + ]); } @@ -33,17 +86,66 @@ public static function table(Table $table): Table { return $table ->columns([ - // + TextColumn::make('id') + ->formatStateUsing(function (VolunteerRequest $record) { + return sprintf('#%d', $record->id); + }) + ->label(__('project.labels.id')) + ->sortable(), + TextColumn::make('volunteer.name') + ->label(__('volunteer.column.name')) + ->searchable(), + TextColumn::make('volunteer.email') + ->label(__('volunteer.column.email')) + ->searchable(), + TextColumn::make('project') + ->label(__('volunteer.column.project')) + ->formatStateUsing(fn ($record) => $record->model_type === 'App\Models\Project' ? self::getResourceLink($record, 'project') : 'General') + ->searchable(), + TextColumn::make('organization_name') + ->label(__('volunteer.column.organization')) + ->formatStateUsing(fn ($record) => self::getResourceLink($record, 'organization')) + ->searchable(), + IconColumn::make('status') + ->label(__('volunteer.column.status')) + ->options([ + 'heroicon-o-clock' => 'pending', + 'heroicon-o-check-circle' => 'approved', + 'heroicon-o-x-circle' => 'rejected', + ])->colors([ + 'warning' => 'pending', + 'success' => 'approved', + 'danger' => 'rejected', + ]), + TextColumn::make('has_user') + ->label(__('volunteer.column.has_user')) + ->formatStateUsing(fn ($record) => self::getResourceLink($record, 'user')), + ]) ->filters([ - // + SelectFilter::make('name') + ->multiple() + ->relationship('volunteer', 'name') + ->label(__('volunteer.filters.user')), + TernaryFilter::make('has_user') + ->label(__('volunteer.filters.has_user')) + ->queries( + true: fn (Builder $query) => $query->whereHas( + 'volunteer', + fn (Builder $query) => $query->whereNotNull('user_id') + ), + false: fn (Builder $query) => $query->whereHas( + 'volunteer', + fn (Builder $query) => $query->whereNull('user_id') + ), + ), + ]) ->actions([ - Tables\Actions\EditAction::make(), - ]) - ->bulkActions([ - Tables\Actions\DeleteBulkAction::make(), - ]); + ViewAction::make()->iconButton(), + EditAction::make()->iconButton(), + DeleteAction::make()->iconButton(), + ])->defaultSort('id', 'desc'); } public static function getRelations(): array @@ -57,8 +159,53 @@ public static function getPages(): array { return [ 'index' => Pages\ListVolunteers::route('/'), - 'create' => Pages\CreateVolunteer::route('/create'), - 'edit' => Pages\EditVolunteer::route('/{record}/edit'), ]; } + + private static function getResourceLink(VolunteerRequest $record, string $type): HtmlString + { + return match ($type) { + 'project' => self::getProjectLink($record), + 'organization' => self::getOrganizationLink($record), + 'user' => self::getUserLink($record), + }; + } + + private static function getProjectLink(VolunteerRequest $record): HtmlString + { + if ($record->model_type === 'App\Models\Project') { + $url = route('filament.resources.projects.view', $record->model_id); + $name = Str::words($record->model->name, 3, '...'); + + return new HtmlString(sprintf('%s', $url, $name)); + } + + return new HtmlString('General'); + } + + private static function getOrganizationLink(VolunteerRequest $record) + { + if ($record->model_type === 'App\Models\Organization') { + $url = route('filament.resources.organizations.view', $record->model->id); + $name = Str::words($record->model->name, 3, '...'); + + return new HtmlString(sprintf('%s', $url, $name)); + } + + $url = route('filament.resources.organizations.view', $record->model->organization->id); + $name = Str::words($record->model->organization->name, 3, '...'); + + return new HtmlString(sprintf('%s', $url, $name)); + } + + private static function getUserLink(VolunteerRequest $record): HtmlString + { + if ($record->volunteer->user_id === null) { + return new HtmlString('Nu'); + } + $url = route('filament.resources.users.view', $record->volunteer->user_id); + $name = Str::words($record->volunteer->name, 3, '...'); + + return new HtmlString(sprintf('%s', $url, $name)); + } } diff --git a/app/Filament/Resources/VolunteerResource/Pages/CreateVolunteer.php b/app/Filament/Resources/VolunteerResource/Pages/CreateVolunteer.php deleted file mode 100644 index 775d6688..00000000 --- a/app/Filament/Resources/VolunteerResource/Pages/CreateVolunteer.php +++ /dev/null @@ -1,13 +0,0 @@ -authorize('editAsNgo', $project); // dd($request->validated()); ProjectService::update($project, $request->validated()); diff --git a/app/Http/Controllers/Dashboard/UserController.php b/app/Http/Controllers/Dashboard/UserController.php index 95a51803..06b9f780 100644 --- a/app/Http/Controllers/Dashboard/UserController.php +++ b/app/Http/Controllers/Dashboard/UserController.php @@ -49,7 +49,7 @@ public function store(Request $request): RedirectResponse ] ); - $user->role=UserRole::MANAGER; + $user->role = UserRole::MANAGER; $user->organization() ->associate(auth()->user()->organization) ->save(); @@ -65,7 +65,7 @@ public function destroy(Request $request, User $user): RedirectResponse $user->organization() ->dissociate() ->save(); - $user->role=UserRole::USER; + $user->role = UserRole::USER; $user->notify(new UserRemovedFromOrganizationNotification()); return redirect()->back() diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 351b9f01..bc06c326 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -71,10 +71,10 @@ protected function projectList(Request $request, string $view): Response public function show(Project $project) { - if (!$project->isApproved() && !auth()->check()) - { - abort(403); + if (! $project->isApproved() && ! auth()->check()) { + abort(403); } + return Inertia::render('Public/Projects/Show', [ 'project' => new ShowProjectResource($project), ]); diff --git a/app/Models/User.php b/app/Models/User.php index f6c1398a..a1eb62f2 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -109,4 +109,14 @@ public function prunable(): Builder ->whereNull('email_verified_at') ->whereNull('password_set_at'); } + + public function scopeWhereHasVerifiedEmail(Builder $query): Builder + { + return $query->whereNotNull('email_verified_at'); + } + + public function scopeWhereDoesntHaveVerifiedEmail(Builder $query): Builder + { + return $query->whereNull('email_verified_at'); + } } diff --git a/app/Models/VolunteerRequest.php b/app/Models/VolunteerRequest.php index 5bd7260a..058c163b 100644 --- a/app/Models/VolunteerRequest.php +++ b/app/Models/VolunteerRequest.php @@ -20,6 +20,10 @@ class VolunteerRequest extends MorphPivot 'status', ]; + protected $with = [ + 'model', + ]; + protected $casts = [ 'status' => VolunteerStatus::class, ]; diff --git a/app/Notifications/Ngo/UserRemovedFromOrganizationNotification.php b/app/Notifications/Ngo/UserRemovedFromOrganizationNotification.php index e701f111..2800ddfc 100644 --- a/app/Notifications/Ngo/UserRemovedFromOrganizationNotification.php +++ b/app/Notifications/Ngo/UserRemovedFromOrganizationNotification.php @@ -7,7 +7,6 @@ use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; -use Illuminate\Support\Facades\URL; class UserRemovedFromOrganizationNotification extends Notification { diff --git a/app/Services/ProjectService.php b/app/Services/ProjectService.php index be45148f..dbe6249a 100644 --- a/app/Services/ProjectService.php +++ b/app/Services/ProjectService.php @@ -84,7 +84,6 @@ private function createDraftProject(array $data): Project|RegionalProject } $data['organization_id'] = auth()->user()->organization_id; - return $this->project::create($data); } diff --git a/lang/ro/user.php b/lang/ro/user.php index b7e6248e..dee618a4 100644 --- a/lang/ro/user.php +++ b/lang/ro/user.php @@ -8,8 +8,7 @@ 'supermanager' => 'Manager Bursa Binelui', 'admin' => 'Administrator ONG', 'manager' => 'Manager ONG', - - 'donor' => 'Donator/Voluntar', + 'user' => 'Donator/Voluntar', ], 'label' => [ @@ -20,6 +19,7 @@ 'general_data' => 'Date generale', 'volunteer_for_organization' => 'Voluntar in organizatie', 'volunteer_for_project' => 'Proiect din organizatia: :organization', + 'organization' => 'Organizatie', ], 'relations' => [ 'donations' => 'Donatii', @@ -39,13 +39,14 @@ 'created' => 'Utilizatorul a fost adăugat.', 'deleted' => 'Utilizatorul a fost șters.', 'set_initial_password_success' => 'Parola a fost setată cu succes!', - 'password_updated_successfully'=> 'Parola a fost actualizată cu succes!', + 'password_updated_successfully' => 'Parola a fost actualizată cu succes!', ], 'filters' => [ 'type' => 'Tip', 'placeholder' => 'Selectează tipul', 'has_donations' => 'Are donații', 'is_volunteer' => 'Este voluntar', + 'has_verified_email' => 'Are email verificat', ], 'column' => [ diff --git a/lang/ro/volunteer.php b/lang/ro/volunteer.php index f58e3786..29a6fa4e 100644 --- a/lang/ro/volunteer.php +++ b/lang/ro/volunteer.php @@ -12,9 +12,24 @@ 'column' => [ 'name' => 'Nume', 'phone' => 'Telefon', + 'email' => 'Email', + 'status' => 'Status', 'project' => 'Proiect', 'created_at' => 'Dată înscriere', 'has_user' => 'Utilizator', + 'organization' => 'Organizație', + ], + + 'form' => [ + 'name' => 'Nume', + 'phone' => 'Telefon', + 'email' => 'Email', + 'status' => 'Status', + 'project' => 'Proiect', + 'created_at' => 'Dată înscriere', + 'has_user' => 'Utilizator', + 'organization' => 'Organizație', + 'model_type' => 'Voluntar pe:', ], 'statuses' => [ @@ -30,4 +45,10 @@ 'rejected' => 'Voluntarul a fost respins.', 'deleted' => 'Voluntarul a fost șters.', ], + 'filters' => [ + 'status' => 'Status', + 'has_user' => 'Este utilizator inregistrat', + 'user' => 'Utilizator', + + ], ];