diff --git a/app/Enums/StockStatusEnum.php b/app/Enums/StockStatusEnum.php new file mode 100644 index 0000000..570faa1 --- /dev/null +++ b/app/Enums/StockStatusEnum.php @@ -0,0 +1,25 @@ + 'Raktáron', + self::OutOfStock => 'Nincs raktáron', + }; + } + + public function color(): string + { + return match ($this) { + self::InStock => 'success', + self::OutOfStock => 'danger', + }; + } +} diff --git a/app/Filament/SupplierPortal/Resources/ProductStocks/Pages/EditProductStock.php b/app/Filament/SupplierPortal/Resources/ProductStocks/Pages/EditProductStock.php new file mode 100644 index 0000000..3584b52 --- /dev/null +++ b/app/Filament/SupplierPortal/Resources/ProductStocks/Pages/EditProductStock.php @@ -0,0 +1,27 @@ +label('Vissza a listához') + ->url(fn () => ProductStockResource::getUrl('index')) + ->color('gray'), + ]; + } + + protected function getRedirectUrl(): string + { + return ProductStockResource::getUrl('index'); + } +} diff --git a/app/Filament/SupplierPortal/Resources/ProductStocks/Pages/ListProductStocks.php b/app/Filament/SupplierPortal/Resources/ProductStocks/Pages/ListProductStocks.php new file mode 100644 index 0000000..3fdf1f0 --- /dev/null +++ b/app/Filament/SupplierPortal/Resources/ProductStocks/Pages/ListProductStocks.php @@ -0,0 +1,11 @@ +where('supplier_id', auth()->user()->supplier_id); + } + + public static function form(Schema $schema): Schema + { + return ProductStockForm::configure($schema); + } + + public static function table(Table $table): Table + { + return ProductStocksTable::configure($table); + } + + public static function getPages(): array + { + return [ + 'index' => ListProductStocks::route('/'), + 'edit' => EditProductStock::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/SupplierPortal/Resources/ProductStocks/Schemas/ProductStockForm.php b/app/Filament/SupplierPortal/Resources/ProductStocks/Schemas/ProductStockForm.php new file mode 100644 index 0000000..ee195dd --- /dev/null +++ b/app/Filament/SupplierPortal/Resources/ProductStocks/Schemas/ProductStockForm.php @@ -0,0 +1,23 @@ +components([ + Select::make('stock_status') + ->label('Készlet státusz') + ->options( + collect(StockStatusEnum::cases()) + ->mapWithKeys(fn (StockStatusEnum $case) => [$case->value => $case->label()]) + ) + ->required(), + ]); + } +} diff --git a/app/Filament/SupplierPortal/Resources/ProductStocks/Tables/ProductStocksTable.php b/app/Filament/SupplierPortal/Resources/ProductStocks/Tables/ProductStocksTable.php new file mode 100644 index 0000000..1d211a0 --- /dev/null +++ b/app/Filament/SupplierPortal/Resources/ProductStocks/Tables/ProductStocksTable.php @@ -0,0 +1,38 @@ +columns([ + TextColumn::make('name') + ->label('Termék neve') + ->searchable() + ->sortable(), + + TextColumn::make('sku') + ->label('Cikkszám') + ->searchable(), + + ToggleColumn::make('stock_status') + ->label('Raktáron') + ->onColor('success') + ->offColor('danger') + ->getStateUsing(fn ($record) => $record->stock_status === StockStatusEnum::InStock) + ->updateStateUsing(function ($record, bool $state) { + $record->stock_status = $state + ? StockStatusEnum::InStock + : StockStatusEnum::OutOfStock; + $record->save(); + }), + ]); + } +} diff --git a/app/Http/Controllers/Admin/SupplierController.php b/app/Http/Controllers/Admin/SupplierController.php index ee34515..2eddfb2 100644 --- a/app/Http/Controllers/Admin/SupplierController.php +++ b/app/Http/Controllers/Admin/SupplierController.php @@ -135,6 +135,11 @@ private function getSupplierDataFromRequest(Request $request) $data['profitCenterId'] = \App\Models\ProfitCenter::whereIn('name', (array) $profitCenterNames)->get()->pluck('id')->toArray(); } + $data['userId'] = []; + if ($userIds = $request->get('userId')) { + $data['userId'] = array_map('intval', (array) $userIds); + } + if ($request->file('logoFile')) { $disk = 'image'; // A Storage::path('') visszaadja a disk root könyvtárát @@ -267,6 +272,31 @@ public function destroy(int $SupplierId): Response|JsonResponse } + public function createUser(): JsonResponse + { + $data = \request()->validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email', 'max:255', 'unique:users,email'], + 'password' => ['required', 'string', 'min:8'], + ]); + + $user = \App\Models\User::create([ + 'name' => $data['name'], + 'email' => $data['email'], + 'password' => \Illuminate\Support\Facades\Hash::make($data['password']), + ]); + + $supplierRole = \App\Models\Role::where('name', 'supplier')->first(); + if ($supplierRole) { + $user->addRole($supplierRole); + } + + return response()->json([ + 'success' => true, + 'user' => ['id' => $user->id, 'name' => $user->name, 'email' => $user->email], + ]); + } + public function getDataTableContent(): JsonResponse { diff --git a/app/Http/Controllers/LoginController.php b/app/Http/Controllers/LoginController.php index 9e5b162..fd33206 100644 --- a/app/Http/Controllers/LoginController.php +++ b/app/Http/Controllers/LoginController.php @@ -30,6 +30,10 @@ public function authenticate(Request $request) )) { $request->session()->regenerate(); + if (Auth::user()->supplier_id !== null) { + return redirect('/supplier-portal/product-stocks'); + } + return redirect()->intended('/'); } diff --git a/app/Models/Product.php b/app/Models/Product.php index ad53a71..1f119a9 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Enums\StockStatusEnum; use Carbon\Carbon; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -15,6 +16,13 @@ class Product extends BaseAuditable protected $guarded = ['id']; // protected $fillable=['name']; + protected function casts(): array + { + return [ + 'stock_status' => StockStatusEnum::class, + ]; + } + public function ProductGroup(): BelongsTo { return $this->belongsTo(ProductGroup::class); diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index 8e069a7..404da01 100644 --- a/app/Models/Supplier.php +++ b/app/Models/Supplier.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class Supplier extends BaseAuditable @@ -43,6 +44,11 @@ public function profitCenter(): BelongsToMany return $this->belongsToMany(ProfitCenter::class)->withTimestamps(); } + public function users(): HasMany + { + return $this->hasMany(User::class); + } + public function attachment(): \Illuminate\Database\Eloquent\Relations\MorphMany { return $this->morphMany(Attachment::class, 'attachable'); diff --git a/app/Models/User.php b/app/Models/User.php index 39717a9..b08f176 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,7 +2,10 @@ namespace App\Models; +use Filament\Models\Contracts\FilamentUser; +use Filament\Panel; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -14,7 +17,7 @@ /** * @property Collection profitCenters */ -class User extends Authenticatable implements LaratrustUser +class User extends Authenticatable implements LaratrustUser, FilamentUser { use HasApiTokens, HasFactory, Notifiable; use HasRolesAndPermissions; @@ -29,6 +32,7 @@ class User extends Authenticatable implements LaratrustUser 'name', 'email', 'password', + 'supplier_id', ]; /** @@ -60,4 +64,18 @@ public function profitCenters(): BelongsToMany { return $this->belongsToMany(ProfitCenter::class, 'profit_center_users')->withTimestamps(); } + + public function supplier(): BelongsTo + { + return $this->belongsTo(Supplier::class); + } + + public function canAccessPanel(Panel $panel): bool + { + return match ($panel->getId()) { + 'admin' => $this->hasRole(['root', 'developer', 'admin', 'profit-center']), + 'supplierPortal' => $this->hasRole('supplier'), + default => false, + }; + } } diff --git a/app/Policies/ProductPolicy.php b/app/Policies/ProductPolicy.php new file mode 100644 index 0000000..2339211 --- /dev/null +++ b/app/Policies/ProductPolicy.php @@ -0,0 +1,75 @@ +hasRole(['root', 'developer', 'admin', 'supplier']); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, Product $product): bool + { + if ($user->hasRole('supplier')) { + return $user->supplier_id === $product->supplier_id; + } + + return $user->hasRole(['root', 'developer', 'admin']); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->hasRole(['root', 'developer', 'admin']); + } + + /** + * Csak akkor engedélyezett a szerkesztés, ha a termék + * a bejelentkezett supplier-hez tartozik. + */ + public function update(User $user, Product $product): bool + { + if ($user->hasRole('supplier')) { + return $user->supplier_id === $product->supplier_id; + } + + return $user->hasRole(['root', 'developer', 'admin']); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Product $product): bool + { + return $user->hasRole(['root', 'developer', 'admin']); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, Product $product): bool + { + return $user->hasRole(['root', 'developer', 'admin']); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, Product $product): bool + { + return $user->hasRole(['root', 'developer', 'admin']); + } +} diff --git a/app/Providers/Filament/SupplierPanelProvider.php b/app/Providers/Filament/SupplierPanelProvider.php new file mode 100644 index 0000000..5e8e448 --- /dev/null +++ b/app/Providers/Filament/SupplierPanelProvider.php @@ -0,0 +1,83 @@ +id('supplierPortal') + ->path('supplier-portal') + ->login() + ->brandName(fn () => auth()->check() && auth()->user()->supplier + ? 'Beszállítói Portál – ' . auth()->user()->supplier->name + : 'Beszállítói Portál') + ->colors([ + 'primary' => '#b83400', + 'gray' => [ + 50 => '#f6f5f0', + 100 => '#eceadf', + 200 => '#d1cfc0', + 300 => '#b4b19b', + 400 => '#9e9b81', + 500 => '#7d775c', + 600 => '#6d6850', + 700 => '#453821', + 800 => '#3a301c', + 900 => '#2d2516', + 950 => '#1f1a0f', + ], + 'danger' => Color::Red, + 'info' => Color::Blue, + 'success' => Color::Emerald, + 'warning' => Color::Orange, + ]) + ->font('Trebuchet MS') + ->discoverResources( + in: app_path('Filament/SupplierPortal/Resources'), + for: 'App\Filament\SupplierPortal\Resources' + ) + ->discoverPages( + in: app_path('Filament/SupplierPortal/Pages'), + for: 'App\Filament\SupplierPortal\Pages' + ) + ->pages([]) + ->discoverWidgets( + in: app_path('Filament/SupplierPortal/Widgets'), + for: 'App\Filament\SupplierPortal\Widgets' + ) + ->widgets([]) + ->middleware([ + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + AuthenticateSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + SubstituteBindings::class, + DisableBladeIconComponents::class, + DispatchServingFilamentEvent::class, + ]) + ->authMiddleware([ + Authenticate::class, + ]) + ->darkMode(false) + ->breadcrumbs(false) + ->navigation(false); + } +} diff --git a/app/Repositories/SupplierRepository.php b/app/Repositories/SupplierRepository.php index 0f0ab21..0e7c4a7 100644 --- a/app/Repositories/SupplierRepository.php +++ b/app/Repositories/SupplierRepository.php @@ -9,7 +9,7 @@ class SupplierRepository extends RepositoryBase implements SupplierRepositoryInt /** * @var array[] */ - private array $defaultRelations = ['contact', 'productGroup', 'attachment', 'service']; + private array $defaultRelations = ['contact', 'productGroup', 'attachment', 'service', 'users']; public function __construct() { @@ -96,6 +96,16 @@ public function syncProfitCenter($supplierId, $profitCenterIds = []): bool return true; } + public function syncUsers($supplierId, $userIds = []): bool + { + \App\Models\User::where('supplier_id', $supplierId)->whereNotIn('id', $userIds)->update(['supplier_id' => null]); + if (count($userIds) > 0) { + \App\Models\User::whereIn('id', $userIds)->update(['supplier_id' => $supplierId]); + } + + return true; + } + /** * Get's a record by it's ID * diff --git a/app/Repositories/SupplierRepositoryInterface.php b/app/Repositories/SupplierRepositoryInterface.php index ebcab78..f27985f 100644 --- a/app/Repositories/SupplierRepositoryInterface.php +++ b/app/Repositories/SupplierRepositoryInterface.php @@ -19,4 +19,6 @@ public function syncProductGroup($supplierId, $productGroupIds = []): bool; public function syncService($supplierId, $productGroupIds = []): bool; public function syncProfitCenter($supplierId, $profitCenterIds = []): bool; + + public function syncUsers($supplierId, $userIds = []): bool; } diff --git a/app/Services/SupplierService.php b/app/Services/SupplierService.php index 78eb2e3..e92df00 100644 --- a/app/Services/SupplierService.php +++ b/app/Services/SupplierService.php @@ -53,13 +53,19 @@ public function syncProfitCenter($supplierId, $profitCenterIds = []): bool return $this->Repository->syncProfitCenter($supplierId, $profitCenterIds); } + public function syncUsers($supplierId, $userIds = []): bool + { + return $this->Repository->syncUsers($supplierId, $userIds); + } + public function add($data): ?int { $contactIds = $data['contactId']; $productGroupId = $data['productGroupId']; $serviceId = $data['serviceId']; $profitCenterId = $data['profitCenterId']; - unset($data['contactId'],$data['productGroupId']); + $userIds = $data['userId'] ?? []; + unset($data['contactId'], $data['productGroupId'], $data['userId']); if (! $supplierId = parent::add($data)) { return false; } @@ -67,6 +73,7 @@ public function add($data): ?int $this->addProductGroup($supplierId, $productGroupId); $this->syncService($supplierId, $serviceId); $this->syncProfitCenter($supplierId, $profitCenterId); + $this->syncUsers($supplierId, $userIds); return $supplierId; } @@ -83,12 +90,14 @@ public function update($id, array $data): bool $productGroupId = $data['productGroupId']; $serviceId = $data['serviceId']; $profitCenterId = $data['profitCenterId']; - unset($data['contactId'],$data['productGroupId']); + $userIds = $data['userId'] ?? []; + unset($data['contactId'], $data['productGroupId'], $data['userId']); if ($this->Repository->update($id, $data)) { $this->syncContact($id, $contactIds); $this->syncProductGroup($id, $productGroupId); $this->syncService($id, $serviceId); $this->syncProfitCenter($id, $profitCenterId); + $this->syncUsers($id, $userIds); return true; } diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 22744d1..92127ea 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -3,4 +3,5 @@ return [ App\Providers\AppServiceProvider::class, App\Providers\Filament\AdminPanelProvider::class, + App\Providers\Filament\SupplierPanelProvider::class, ]; diff --git a/database/migrations/2026_04_25_163803_add_supplier_id_to_users_table.php b/database/migrations/2026_04_25_163803_add_supplier_id_to_users_table.php new file mode 100644 index 0000000..b9045e2 --- /dev/null +++ b/database/migrations/2026_04_25_163803_add_supplier_id_to_users_table.php @@ -0,0 +1,29 @@ +foreignId('supplier_id')->nullable()->after('id')->constrained('suppliers')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropForeign(['supplier_id']); + $table->dropColumn('supplier_id'); + }); + } +}; diff --git a/database/migrations/2026_04_25_164154_add_stock_status_to_products_table.php b/database/migrations/2026_04_25_164154_add_stock_status_to_products_table.php new file mode 100644 index 0000000..d58cb35 --- /dev/null +++ b/database/migrations/2026_04_25_164154_add_stock_status_to_products_table.php @@ -0,0 +1,28 @@ +string('stock_status')->default('in_stock')->after('status'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->dropColumn('stock_status'); + }); + } +}; diff --git a/database/seeders/SupplierRoleSeeder.php b/database/seeders/SupplierRoleSeeder.php new file mode 100644 index 0000000..0d607d1 --- /dev/null +++ b/database/seeders/SupplierRoleSeeder.php @@ -0,0 +1,34 @@ + 'update_own_product_stock_status'], + [ + 'display_name' => 'Saját termék készlet státusz módosítása', + 'description' => 'A beszállító módosíthatja a saját termékei raktárkészlet státuszát', + ] + ); + + $role = Role::firstOrCreate( + ['name' => 'supplier'], + [ + 'display_name' => 'Beszállító', + 'description' => 'Supplier Portal hozzáférés', + ] + ); + + $role->givePermission($permission); + } +} diff --git a/resources/js/admin/supplier.js b/resources/js/admin/supplier.js index b7756ea..2028b58 100644 --- a/resources/js/admin/supplier.js +++ b/resources/js/admin/supplier.js @@ -51,6 +51,7 @@ window.SupplierAdmin=function (options,SupplierItem) { destroy:'', attachFile:'', detachFile:'', + createUser:'', }, dataTable:{ orderDirective:[1, 'asc'] @@ -255,6 +256,9 @@ window.SupplierAdmin=function (options,SupplierItem) { if(typeof root.Supplier['logoFile']!='undefined' && (root.Supplier['logoFile'])){ $(CSSSelectorForm+' img.imageLogo').attr('src',root.Supplier['logoFile']); } + if(typeof root.Supplier.users !== 'undefined'){ + root.resetUserTable(); + } //debug(APP.SupplierAdmin.formDataToDataObjectAPICall('.supplierForm')['productGroup']); //$('[name=productGroup]').val(['Büféáru_édesipari_termékek','Tojás']).change(); @@ -300,6 +304,7 @@ window.SupplierAdmin=function (options,SupplierItem) { $('form.supplierForm button.FormSaveButton').off('click'); $('form.supplierForm button.FormSaveButton').on('click',root.clickSaveButton); root.setDetailsViewData(); + root.initUserCreate(); $(".productCategorySelect").select2(); $(".serviceSelect").select2({ tags: true, @@ -834,5 +839,93 @@ window.SupplierAdmin=function (options,SupplierItem) { } + this.resetUserTable=function (){ + $('.supplierUserTable tbody tr').remove(); + root.renderUsers(); + } + this.renderUsers=function (){ + let rowNum=1; + let users=root.Supplier.users||[]; + $.each(users,function (){ + let userData=$.extend({},this); + userData.rowNum=rowNum; + let template=$('template[data-name="supplierUserTableRow"]').html(); + let render=Mustache.render(template,userData); + $('.supplierUserTable tbody').append(render); + rowNum++; + }); + root.bindUserClickAction(); + } + this.bindUserClickAction=function (){ + let node=$('.supplierUserTable .buttonDelete'); + node.unbind('click'); + node.bind('click',root.userDeleteClick); + } + this.userDeleteClick=function (){ + let row=$(this).closest('tr'); + let id=row.find('input[name=userId]').val()*1; + root.Supplier.users=root.Supplier.users.filter(function (value){ + return value.id!==id; + }); + row.remove(); + root.resetUserTable(); + } + this.addUser=function (userData){ + if(!root.Supplier.users){ root.Supplier.users=[]; } + let alreadyAdded=root.Supplier.users.some(function(u){ return u.id===userData.id; }); + if(alreadyAdded){ return; } + root.Supplier.users.push(userData); + userData.rowNum=root.Supplier.users.length; + let template=$('template[data-name="supplierUserTableRow"]').html(); + let render=Mustache.render(template,userData); + $('.supplierUserTable tbody').append(render); + root.bindUserClickAction(); + Toast.enableTimers(false); + Toast.create({ title: 'Felhasználó hozzáadva!', message: userData.name+' hozzárendelve', status: TOAST_STATUS.SUCCESS, timeout: 3000 }); + } + this.initUserCreate=function (){ + let createUrl=Opts.URL.createUser||''; + let container=$(Opts.containerCssSelector); + function clearForm(){ + container.find('.supplierNewUserName').val(''); + container.find('.supplierNewUserEmail').val(''); + container.find('.supplierNewUserPassword').val(''); + container.find('.supplierNewUserError').hide().text(''); + } + container.find('.buttonCreateUser').off('click').on('click',function(){ + let name=container.find('.supplierNewUserName').val().trim(); + let email=container.find('.supplierNewUserEmail').val().trim(); + let password=container.find('.supplierNewUserPassword').val(); + let errorEl=container.find('.supplierNewUserError'); + errorEl.hide().text(''); + if(!name||!email||!password){ + errorEl.text('Minden mező kitöltése kötelező!').show(); + return; + } + $.ajax({ + url:createUrl, + type:'POST', + data:{name:name, email:email, password:password}, + headers:{'X-CSRF-TOKEN': $('input[name="_token"]').val()}, + success:function(data){ + if(data.success){ + root.addUser(data.user); + clearForm(); + }else{ + errorEl.text('Hiba történt a felhasználó létrehozásakor!').show(); + } + }, + error:function(xhr){ + let msg='Hiba történt!'; + if(xhr.responseJSON&&xhr.responseJSON.errors){ + let errors=xhr.responseJSON.errors; + let firstKey=Object.keys(errors)[0]; + msg=errors[firstKey][0]; + } + errorEl.text(msg).show(); + } + }); + }); + } this.construct(options,SupplierItem); }; diff --git a/resources/views/admin/supplier/create.blade.php b/resources/views/admin/supplier/create.blade.php index e115320..bc35e48 100644 --- a/resources/views/admin/supplier/create.blade.php +++ b/resources/views/admin/supplier/create.blade.php @@ -118,6 +118,71 @@ +
+ +
+
+
Új felhasználó hozzáadása
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+
+
+
+ + + + + + + + + + + + +
#NévE-mailAkció
+
+
+
+
+
diff --git a/resources/views/admin/supplier/index.blade.php b/resources/views/admin/supplier/index.blade.php index d7e87ef..ad9b0d9 100644 --- a/resources/views/admin/supplier/index.blade.php +++ b/resources/views/admin/supplier/index.blade.php @@ -81,6 +81,7 @@ destroy:'{{route('admin.supplier.destroy',"%id%")}}', attachFile:'{{route('admin.supplier.attachFile',"%id%")}}', detachFile:'{{route('admin.supplier.detachFile',"%id%")}}', + createUser:'{{route('admin.supplier.createUser')}}', }, testMode:{{$testMode}} },APP.Supplier); diff --git a/routes/web.php b/routes/web.php index 7673a9f..9ade74a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -186,6 +186,7 @@ function () { // Route::resource('/supplier', SupplierControllerAdmin::class); Route::prefix('supplier')->name('supplier.')->group(function () { Route::get('getDataTableContent', [SupplierControllerAdmin::class, 'getDataTableContent'])->name('getDataTableContent'); + Route::post('createUser', [SupplierControllerAdmin::class, 'createUser'])->name('createUser'); Route::post('/{id}/attachFile', [SupplierControllerAdmin::class, 'attachFile'])->name('attachFile'); Route::post('/{id}/detachFile', [SupplierControllerAdmin::class, 'detachFile'])->name('detachFile'); Route::resource('/', SupplierControllerAdmin::class)->parameters(['' => 'supplier']);