Integrate multi-currency debt visibility across the platform by adding support for foreign currency credits in the user profile shortcode, improving the admin dashboard with unsettled credits widgets, and enhancing the Filament admin panel with navigation badges and relation managers. - Add multi-currency debt table to WordPress user profile shortcode - Implement `UnsettledCreditsWidget` for Filament dashboard - Add navigation badge to `CustomerCreditResource` for open credits - Add `CreditsRelationManager` to `UserResource` - Update `CustomerOrderController` API to include `foreign_credits` - Add concurrency check in `CustomerCreditService` during settlement
273 lines
11 KiB
PHP
273 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Resources;
|
|
|
|
use App\Filament\Resources\UserResource\Pages;
|
|
use App\Filament\Resources\UserResource\RelationManagers;
|
|
use App\Models\User;
|
|
use Filament\Forms;
|
|
use Filament\Forms\Form;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
use Filament\Tables\Actions\Action;
|
|
use Filament\Notifications\Notification;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Spatie\Permission\Models\Role;
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
|
|
class UserResource extends Resource
|
|
{
|
|
protected static ?string $model = User::class;
|
|
|
|
protected static ?string $navigationIcon = 'heroicon-o-users';
|
|
protected static ?string $navigationLabel = 'کاربران';
|
|
protected static ?string $modelLabel = 'کاربر';
|
|
protected static ?string $pluralModelLabel = 'کاربران';
|
|
protected static ?string $navigationGroup = 'تنظیمات';
|
|
protected static ?int $navigationSort = 1;
|
|
|
|
public static function form(Form $form): Form
|
|
{
|
|
return $form
|
|
->schema([
|
|
Forms\Components\Section::make('اطلاعات اصلی')
|
|
->schema([
|
|
Forms\Components\TextInput::make('name')
|
|
->label('نام و نام خانوادگی')
|
|
->required()
|
|
->maxLength(255),
|
|
|
|
Forms\Components\TextInput::make('email')
|
|
->label('ایمیل')
|
|
->email()
|
|
->required()
|
|
->unique(ignoreRecord: true)
|
|
->maxLength(255),
|
|
|
|
Forms\Components\TextInput::make('phone')
|
|
->label('شماره موبایل')
|
|
->tel()
|
|
->maxLength(20),
|
|
|
|
Forms\Components\TextInput::make('password')
|
|
->label('رمز عبور')
|
|
->password()
|
|
->revealable()
|
|
->required(fn (string $operation): bool => $operation === 'create')
|
|
->dehydrated(fn ($state): bool => filled($state))
|
|
->dehydrateStateUsing(fn ($state): string => Hash::make($state))
|
|
->helperText('برای تغییر رمز عبور، رمز جدید را وارد کنید. در غیر این صورت خالی بگذارید.'),
|
|
])->columns(2),
|
|
|
|
Forms\Components\Section::make('نقش و دسترسیها')
|
|
->schema([
|
|
Forms\Components\Select::make('roles')
|
|
->label('نقشهای کاربر')
|
|
->relationship('roles', 'name')
|
|
->multiple()
|
|
->preload()
|
|
->searchable()
|
|
->required()
|
|
->minItems(1)
|
|
->helperText('حداقل یک نقش باید انتخاب شود'),
|
|
|
|
Forms\Components\Toggle::make('is_active')
|
|
->label('وضعیت فعال')
|
|
->default(true)
|
|
->helperText('کاربران غیرفعال نمیتوانند وارد سیستم شوند'),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->columns([
|
|
Tables\Columns\TextColumn::make('id')
|
|
->label('شناسه')
|
|
->sortable()
|
|
->searchable()
|
|
->toggleable(isToggledHiddenByDefault: true),
|
|
|
|
Tables\Columns\TextColumn::make('name')
|
|
->label('نام')
|
|
->searchable()
|
|
->sortable(),
|
|
|
|
Tables\Columns\TextColumn::make('email')
|
|
->label('ایمیل')
|
|
->searchable()
|
|
->sortable()
|
|
->copyable()
|
|
->copyMessage('ایمیل کپی شد')
|
|
->copyMessageDuration(1500),
|
|
|
|
Tables\Columns\TextColumn::make('phone')
|
|
->label('موبایل')
|
|
->searchable()
|
|
->toggleable(),
|
|
|
|
Tables\Columns\TextColumn::make('roles.name')
|
|
->label('نقشها')
|
|
->badge()
|
|
->color(fn (string $state): string => match ($state) {
|
|
'super_admin' => 'danger',
|
|
'admin' => 'warning',
|
|
'staff' => 'info',
|
|
'customer' => 'success',
|
|
default => 'gray',
|
|
})
|
|
->separator('، '),
|
|
|
|
Tables\Columns\IconColumn::make('is_active')
|
|
->label('فعال')
|
|
->boolean()
|
|
->sortable()
|
|
->trueIcon('heroicon-o-check-circle')
|
|
->falseIcon('heroicon-o-x-circle')
|
|
->trueColor('success')
|
|
->falseColor('danger'),
|
|
|
|
Tables\Columns\TextColumn::make('created_at')
|
|
->label('تاریخ عضویت')
|
|
->dateTime('Y/m/d')
|
|
->sortable()
|
|
->toggleable(isToggledHiddenByDefault: true),
|
|
])
|
|
->filters([
|
|
Tables\Filters\SelectFilter::make('role')
|
|
->label('نقش')
|
|
->options([
|
|
'super_admin' => 'مدیر ارشد',
|
|
'admin' => 'مدیر',
|
|
'staff' => 'کارمند',
|
|
'customer' => 'مشتری',
|
|
])
|
|
->query(function ($query, array $data) {
|
|
return $query->when($data['value'], function ($query, $role) {
|
|
return $query->role($role);
|
|
});
|
|
}),
|
|
|
|
Tables\Filters\TernaryFilter::make('is_active')
|
|
->label('وضعیت فعال'),
|
|
])
|
|
->actions([
|
|
Tables\Actions\EditAction::make()
|
|
->label('ویرایش'),
|
|
|
|
Action::make('toggleActive')
|
|
->label(fn ($record) => $record->is_active ? 'غیرفعال کردن' : 'فعال کردن')
|
|
->icon(fn ($record) => $record->is_active ? 'heroicon-o-x-circle' : 'heroicon-o-check-circle')
|
|
->color(fn ($record) => $record->is_active ? 'danger' : 'success')
|
|
->requiresConfirmation()
|
|
->action(function (User $record) {
|
|
$record->update(['is_active' => !$record->is_active]);
|
|
|
|
Notification::make()
|
|
->title($record->is_active ? 'کاربر فعال شد' : 'کاربر غیرفعال شد')
|
|
->success()
|
|
->send();
|
|
}),
|
|
|
|
Action::make('resetPassword')
|
|
->label('بازنشانی رمز')
|
|
->icon('heroicon-o-key')
|
|
->color('warning')
|
|
->form([
|
|
Forms\Components\TextInput::make('password')
|
|
->label('رمز عبور جدید')
|
|
->password()
|
|
->revealable()
|
|
->required()
|
|
->minLength(8),
|
|
])
|
|
->requiresConfirmation()
|
|
->action(function (User $record, array $data) {
|
|
$record->update([
|
|
'password' => Hash::make($data['password']),
|
|
]);
|
|
|
|
Notification::make()
|
|
->title('رمز عبور با موفقیت بازنشانی شد')
|
|
->success()
|
|
->send();
|
|
}),
|
|
])
|
|
->bulkActions([
|
|
Tables\Actions\BulkActionGroup::make([
|
|
Tables\Actions\DeleteBulkAction::make()
|
|
->label('حذف انتخابشدهها'),
|
|
]),
|
|
])
|
|
->defaultSort('created_at', 'desc');
|
|
}
|
|
|
|
public static function getRelations(): array
|
|
{
|
|
return [
|
|
RelationManagers\CreditsRelationManager::class,
|
|
];
|
|
}
|
|
|
|
public static function getPages(): array
|
|
{
|
|
return [
|
|
'index' => Pages\ListUsers::route('/'),
|
|
'create' => Pages\CreateUser::route('/create'),
|
|
'edit' => Pages\EditUser::route('/{record}/edit'),
|
|
];
|
|
}
|
|
|
|
// ─── Access Control ───
|
|
|
|
protected static function getCurrentUser(): ?\App\Models\User
|
|
{
|
|
$user = Auth::user();
|
|
return $user instanceof \App\Models\User ? $user : null;
|
|
}
|
|
|
|
// فقط super_admin و admin میتوانند کاربران را ببینند
|
|
public static function canViewAny(): bool
|
|
{
|
|
$user = static::getCurrentUser();
|
|
return $user !== null && $user->hasAnyRole(['super_admin', 'admin']);
|
|
}
|
|
|
|
// فقط super_admin میتواند کاربر جدید بسازد
|
|
public static function canCreate(): bool
|
|
{
|
|
$user = static::getCurrentUser();
|
|
return $user !== null && $user->hasRole('super_admin');
|
|
}
|
|
|
|
// super_admin و admin میتوانند ویرایش کنند
|
|
public static function canEdit($record): bool
|
|
{
|
|
$user = static::getCurrentUser();
|
|
if ($user === null) return false;
|
|
|
|
// admin نمیتواند super_admin را ویرایش کند
|
|
if ($user->hasRole('admin') && $record->hasRole('super_admin')) {
|
|
return false;
|
|
}
|
|
|
|
return $user->hasAnyRole(['super_admin', 'admin']);
|
|
}
|
|
|
|
// فقط super_admin میتواند کاربر حذف کند
|
|
public static function canDelete($record): bool
|
|
{
|
|
$user = static::getCurrentUser();
|
|
if ($user === null) return false;
|
|
|
|
// super_admin نمیتواند خودش را حذف کند
|
|
if ($record->id === $user->id) {
|
|
return false;
|
|
}
|
|
|
|
return $user->hasRole('super_admin');
|
|
}
|
|
} |