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',
+
+ ],
];